在Rust(以及其他编程语言)中,关键字await用于指示异步函数体中的挂起点.然而,我注意到,与其他编程语言(Pythongather、Swift withTaskGroup、JAVASCRIPTPromise.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!宏没有返回某种可以被await编辑的聚合Future或联接句柄?

隐式await令人困惑,因为这个异步函数乍一看似乎没有任何挂起点(因此不需要是async),我认为这可能会违背async-await模式寻求显式设置挂起点的目的.

推荐答案

如果展开宏(可以在铁 rust playground 上的工具菜单上完成),您将看到如下所示的代码:

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 7079bcd60975f592e08fcd575991f6ae2a409a1f年(PR #2158年)引入的,在那里没有太多的讨论,但它在commit d67e2936c21a4d663814e38b06ce38d85bb02e9b年(PR #futures1年)引入的futures库中有一个前身.

关于最初的实现,需要注意的一点是,与Tokio中当前的实现不同,它is在很大程度上基于异步上下文(poll!pending!宏依赖于它):

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

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

comments 于2018年6月28日

  • 在用于异步函数的宏和其他宏之间可能应该有一些明确的区别.内部使用await的宏应该以某种方式进行标记.或者,他们都可以使用async街区,并要求await!(join![a, b])周围有await街区.这看起来很糟糕,因为await!目前是一个宏,但它不会在future 出现.然而,心理模型更简单,您可以在任何地方使用它,而不仅仅是在异步函数中.

comments 于2018年6月28日

@MajorBreakfast select!需要在内部执行await!(而不是提供future 的结果),以便允许编译器更智能地处理控制流(例如,允许return正常工作,启用初始化状态判断以了解可能发生中断的不同条件,等等).既然select!不能返回future ,我认为join!返回future 会让人困惑--这两者看起来有点像自然的替身,在这方面他们的工作方式似乎不同,这似乎令人惊讶.

因此,select!受益于不引入新的异步块,而是将其自身合并到调用方的异步块中,而join!被设计为具有与select!相同的特征,尽管它本身并不受益.

Rust相关问答推荐

Rust kill std::processs::child

将数组转换为HashMap的更简单方法

如何模拟/创建ReqData以测试Actix Web请求处理程序?

如何高效地将 struct 向量中的字段收集到单独的数组中

是否可以在不切换到下一个位置的情况下获得迭代器值:

我是否可以在Ruust中修改 struct 实例上的字符串,以使其在修改后具有相同的字符串生存期?

有没有可能让泛型Rust T总是堆分配的?

变量需要parse()中的显式类型

将serde_json读入`VEC<;T&>;`( rust 色)时出现问题

在Rust内联程序集中使用字符串常量

什么时候使用FuturesOrdered?

如何迭代存储在 struct 中的字符串向量而不移动它们?

当没有实际结果时,如何在 Rust 中强制执行错误处理?

Rust中的一生语法有什么作用?

如何在 Rust 中将枚举变体转换为 u8?

为什么这个值在上次使用后没有下降?

为什么我可以从读取的可变自引用中移出?

如何构建包含本地依赖项的 docker 镜像?

如何制作具有关联类型的特征的类型擦除版本?

如何阅读 HttpRequest 主体