有没有办法像Go一样使用Rust的频道?我找不到任何东西.
对于那些不熟悉Go中的select语句的人(来自文档):
"select"语句选择一组可能的发送或接收操作中的哪一个将继续.它看起来类似于"switch"语句,但所有情况都涉及通信操作.具有RecvStmt的情况可以将RecvExpr的结果分配给一个或两个变量,这些变量可以使用短变量声明来声明.RecvExpr必须是(可能是带括号的)接收操作.最多只能有一个默认情况,它可能出现在案例列表中的任何位置.
执行"select"语句分几步进行:
- 对于语句中的所有情况,接收操作的通道操作数以及发送语句的通道和右侧表达式在输入"select"语句后按源顺序精确计算一次.结果是一组要接收或发送的通道,以及要发送的相应值.无论选择哪种(如果有的话)通信操作进行,评估中的任何副作用都将发生.尚未评估具有短变量声明或赋值的RecvStmt左侧的表达式.
- 如果一个或多个通信可以继续,则可以通过统一的伪随机选择来选择可以继续的单个通信.否则,如果存在默认情况,则选择该情况.如果没有默认情况,则"select"语句将阻塞,直到至少一个通信可以继续.
- 除非所选择的情况是默认情况,否则执行相应的通信操作.
- 如果所选案例是具有短变量声明或赋值的RecvStmt,则评估左侧表达式并分配接收值(或多个值).
- 执行所选案例的语句列表.
由于nil通道上的通信永远不会进行,因此只选择nil通道并且永远不会出现默认情况.
例如,我怎么能在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
}
Run Code Online (Sandbox Code Playgroud)
对于标准库中的通道,最有用的答案是“没有一个”。
技术上正确的答案是select!宏:
select! {
r = result.recv() => {
// do something
}
_ = something_that_waits_a_second.recv() => {
// timeout
}
}
Run Code Online (Sandbox Code Playgroud)
(请注意,这相当于OP 的原始示例,在它被彻底改变之前)。
它不稳定,这就是我将其归类为无用的原因。
除了稳定性之外,select!还有其他问题。例如,在您的 Go 示例中,您创建了一些会在一段时间后神奇地提交到频道的内容 ( time.After(time.Second))。Rust 没有始终运行的运行时来驱动这些东西。这意味着您需要生成一个操作系统级线程以等待一段时间并将一个值推送到通道中以执行该超时!这是相当低效的。
如果您真的正在寻找更接近 Go 的绿色线程的东西,我建议您改为研究期货:
select! {
r = result.recv() => {
// do something
}
_ = something_that_waits_a_second.recv() => {
// timeout
}
}
Run Code Online (Sandbox Code Playgroud)
该#[tokio::main]注释是在每一个围棋程序隐含的; 那是启动一个异步反应器并运行它“完成”。
如果您只想在超时时收到,则该recv_timeout方法就是您要找的方法.
try_recv如果您想在不阻止的情况下接收,还有一种方法.
如果你想混合几个频道,那么@ Shepmaster的答案描述了我在标准库中知道的唯一方法.
然而Rust的标准库比Go的重量要轻得多,而在Rust中,通常使用crates来做这种事情.该crossbeam_channel箱子确实有一个select是稳定的,可以用来等待不同的通道.