是否可以使用Rust的频道(例如Go)?我找不到任何东西。
对于那些不熟悉Go中的select语句(来自the documentation)的人:
“ select”语句选择一组可能的发送或接收操作中的哪一个进行。它看起来类似于“ switch”语句,但所有情况均涉及通信操作。 具有RecvStmt的案例可以将RecvExpr的结果分配给一个或两个变量,可以使用短变量声明来声明。 RecvExpr必须是一个(可能带有括号的)接收操作。默认情况下最多可以有一个案例,它可能出现在案例列表的任何地方。
“ select”语句的执行分几个步骤进行:
- 对于该语句中的所有情况,输入“ select”语句后,接收操作的通道操作数以及send语句的通道表达式和右侧表达式将按源顺序进行一次精确评估。结果是一组要从中接收或发送到的通道,以及要发送的相应值。不管选择进行哪个通信操作,都会发生该评估中的任何副作用。尚未评估带有简短变量声明或赋值的RecvStmt左侧的表达式。
如果可以进行一种或多种通信,则可以通过统一的伪随机选择来选择可以进行的单个通信。否则,如果存在默认情况,则选择该情况。如果没有默认情况,则“ select”语句将阻塞,直到可以进行至少一种通信为止。- 除非选择的案例为默认案例,否则将执行相应的通信操作。
- 如果所选案例是带有简短变量声明或赋值的RecvStmt,则将评估左侧表达式并分配接收值(或多个值)。
- 执行所选案例的陈述列表。
由于零通道上的通信永远无法进行,因此只有零通道且没有默认情况下的选择永远不会阻塞。
例如,如何在Rust中编写此代码?
func search(ctx context.Context, result chan IResult, q string) error {
// googleSearch and bingSearch will return IResult interface channel
google := googleSearch(q)
bing := bingSearch(q)
t := time.After(time.Second)
for {
select {
// at any point if caller cancel the operation we return
case <- ctx.Done():
return nil
case r, ok := <- google:
if !ok { // check if channel is closed
google = nil
if bing == nil { // we are done
return nil
}
continue
}
// sending the google search result to result channel. ( channel to channel )
result <- r
case r, ok := <- bing:
if !ok {
bing = nil
if google == nil {
return nil
}
continue
}
result <- r
case <- t:
// this function never lives for more then 1 second
return fmt.Errorf("timeout")
}
}
return nil
}
答案 0 :(得分:5)
如果您只想超时接收,recv_timeout
方法就是您想要的。
如果您想不加干扰地接收,还有一个try_recv
方法。
如果要混合使用多个通道,则@Shepmaster's answer描述了我在标准库中了解的唯一方法。
然而,Rust的标准库比Go的轻巧得多,在Rust中,通常使用包装箱来进行这种处理。 crossbeam_channel
板条箱确实有一个select
稳定,可以用来在不同的频道上等待。
答案 1 :(得分:4)
对于标准库中的频道,最有用的答案是“没有一个”。
技术上正确的答案是select!
宏:
select! {
r = result.recv() => {
// do something
}
_ = something_that_waits_a_second.recv() => {
// timeout
}
}
(请注意,这与OP's original example等效,之前已被彻底更改)。
它很不稳定,这就是为什么我将其归类为无用。
除了select!
的稳定性之外,还有其他问题。例如,在您的Go示例中,您创建了一些将在一段时间后{time.After(time.Second)
神奇地提交到频道的内容。 Rust没有始终运行的运行时来驱动这些事情。这意味着您需要产生一个OS级线程来等待一段时间,然后将一个值推入通道以执行该超时!这是非常低效的。
如果您确实在寻找与Go的绿色路线更接近的东西,我建议您研究一下期货:
extern crate futures; // 0.1.23
extern crate tokio; // 0.1.8
use futures::{future::Either, prelude::*, stream};
use std::time::{Duration, Instant};
use tokio::timer::{Delay, Interval, Timeout};
fn example() -> impl Stream<Item = String, Error = &'static str> {
let goog = google_search();
let bing = bing_search();
let combined = goog.select(bing);
let deadline = Delay::new(Instant::now() + Duration::from_secs(1))
.map_err(drop)
.into_stream();
let combined = combined.map(Either::A);
let deadline = deadline.map(Either::B);
combined.select(deadline).then(|r| match r {
Ok(Either::A(r)) => Ok(r),
Ok(Either::B(_)) => Err("timeout"),
Err(_) => Err("unexpected error"),
})
}
fn main() {
tokio::run({
example()
.map_err(|e| println!("Got an error: {}", e))
.for_each(|r| {
println!("Search result: {}", r);
Ok(())
})
});
}
fn google_search() -> impl Stream<Item = String, Error = ()> {
let results = (0..10).map(|x| format!("goog{}", x));
let results = stream::iter_ok(results);
// Fake some delay between results
let delay = Interval::new_interval(Duration::from_millis(75));
results.zip(delay).map(|(r, _)| r).map_err(drop)
}
fn bing_search() -> impl Stream<Item = String, Error = ()> {
let results = (0..10).map(|x| format!("bing{}", x));
let results = stream::iter_ok(results);
// Fake some delay between results
let delay = Interval::new_interval(Duration::from_millis(200));
results.zip(delay).map(|(r, _)| r).map_err(drop)
}
在每个Go程序中都隐含了对tokio::run
的调用;这将启动一个异步反应堆并“完成”运行。