为什么`时雄::不加入!` macro在Rust中是否需要`await`关键字?

pinkon5k  于 2023-05-29  发布在  Mac
关注(0)|答案(1)|浏览(211)

在Rust(以及其他编程语言)中,await关键字用于指示异步函数体中的挂起点。然而,我注意到,与其他编程语言(Python gather,Swift withTaskGroup,JavaScript Promise.all等)相反,Rust不需要并且会拒绝在Tokyo::join!宏之后出现await
例如,在这个async函数中,在join!之后没有涉及await

use tokio::{io::Result, time::{sleep, Duration}, join};

async fn work() -> String {
    sleep(Duration::from_secs(2)).await;
    String::from("Work done")
}

#[tokio::main]
async fn main() -> Result<()> {
    println!("Awaiting…");
    let (o1, o2) = join!(
        work(),
        work(),
    );  // No `.await`
    println!("{}, {}", o1, o2);
    Ok(())
}

是否有技术原因导致join!宏不返回某种聚合的Future或可以被await绑定的连接句柄?
这种隐式的await令人困惑,因为这个异步函数乍一看似乎没有任何挂起点(因此不需要是async),我认为这可能会破坏async-await模式的目的,即寻求使挂起点显式。

nimxete2

nimxete21#

如果展开宏(可以在Rust Playground的Tools菜单中完成),您将看到如下代码:

let (o1, o2) = {
    // [some imports]
    let mut futures = (maybe_done(work()), maybe_done(work()));
    // [some more setup]
    poll_fn(move |cx| {
        // [innards]
    }).await
};

最后的await是展开式中唯一的await。因此,没有任何技术原因可以解释为什么宏不能被写入不包含await。然而,让我们看看历史。
join宏是在commit 7079bcd60975f592e08fcd575991f6ae2a409a1fPR #2158)中引入的,在那里没有太多的讨论,但它在futures库中有一个前身,在commit d67e2936c21a4d663814e38b06ce38d85bb02e9bPR #1051)中引入。
关于原始实现需要注意的一点是,与时雄中当前的实现不同,它 * 是 * 严重基于异步上下文(poll!pending!宏依赖于它):

...
loop {
    let mut all_done = true;
    $(
        if let ::core::task::Poll::Pending = poll!($fut.reborrow()) {
            all_done = false;
        }
    )*
...

此外,PR中还进行了一些讨论:

MajorBreakfast于2018年6月28日评论

  • 在异步函数中使用的宏和其他宏之间可能应该有一些明确的区别。内部使用await的宏应该以某种方式标记。或者,它们都可以使用async块,并需要await围绕它们await!(join![a, b])。这看起来很糟糕,因为await!目前是一个宏,但将来不会是。心理模型更简单,你可以在任何地方使用它,而不仅仅是在异步函数中。
    cramertj发表于2018年6月28日

@MajorBreakfast select!需要在内部执行await!(而不是提供一个future作为结果),以允许编译器更智能地控制流(例如:允许return正常工作,使初始化检查能够理解可能发生中断的不同条件等)。由于select!不返回一个future,我认为join!返回一个future会令人困惑--这两者看起来有点像自然的double,而且令人惊讶的是,它们在这方面的工作方式不同。
因此,select!受益于不引入新的异步块,而是将其自身合并到调用者的异步块中,并且join!被设计为具有与select!相同的字符,即使它本身没有受益。

相关问题