`|_| async move {}` 和 `async move |_| {}` 之间有什么区别?

56

让我们看下以下例子:

main.rs

use futures::executor::block_on;
use futures::future::{FutureExt, TryFutureExt};


async fn fut1() -> Result<String, u32> {
  Ok("ok".to_string())
}

fn main() {
    println!("Hello, world!");
    match block_on(fut1().and_then(|x| async move { Ok(format!("{} is \"ok\"", x)) })) {
      Ok(s) => println!("{}", s),
      Err(u) => println!("{}", u)
    };
}

Cargo.toml

[dependencies]
futures = "^0.3"

我问的是表达式|x| async move {}而不是async move |x| {}。后者更明显,但会遇到编译错误:

error[E0658]: async closures are unstable

我想知道 async move || {}|| async move {} 之间有什么区别。它们似乎都是使用 move 关键字的闭包。

$ rustc --version
rustc 1.39.0 (4560ea788 2019-11-04)
2个回答

49

其中一个是异步块(准确来说是具有异步块体的闭包),而另一个是异步闭包。根据async/await RFC:

async ||闭包

除了函数外,还可以将async应用于闭包。与异步函数类似,异步闭包的返回类型为impl Future<Output = T>,而不是T

另一方面:

async blocks

You can create a future directly as an expression using an async block. This form is almost equivalent to an immediately-invoked async closure:

 async { /* body */ }

 // is equivalent to

 (async || { /* body */ })()

except that control-flow constructs like return, break and continue are not allowed within body.

这里的move关键字是指异步闭包和块需要捕获其所关闭变量的所有权。
显然,异步闭包仍被认为是不稳定的。它有此跟踪问题

1
那么现在导入它没有任何区别,对吧? - unegare
它们两者都会立即转换为一个Future,无论是否获取了一些周围变量。除了不稳定的异步闭包外,异步块与获取外部变量是相同的,不是吗? - unegare
17
我认为在这两种情况下捕获变量是有很大不同的。async move || ...会将变量从封闭块移动到闭包中,而|| async move {...}则会将变量从闭包中移动到异步块中。如果你想将它们从封闭块移动到异步块中,我认为现在需要使用move || async move {...} - Sven Marnach
@SvenMarnach,起初我也是这么想的,但这种方式行不通,请查看此链接。在计算异步块的普通闭包文字中,您不需要添加额外的移动。它应该在示例中使用global_string的引用,但它只是移动了。 - Ömer Erden
1
async 块中的代码只有在我 await 未来时才会启动,对吗? - Mr.Wang from Next Door
显示剩余9条评论

0

关于此处之前的评论,似乎存在一些涉及正确语法的"问题"

(move |x| async move {foo(x)})(x).await

应该完全等同于

(move |x| {async move {foo(x)}})(x).await

由于受保护的闭包显然不能在 guard 外部声明为 async,而在 guard 内部声明为 async 则会简单地打开另一个代码块,省略大括号。第二个 move 将所有受影响的变量 -- 已经移动到闭包中 -- 移动到异步的 "安全空间" 中,因此似乎所有可能的 async move 保护都应用在这里。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接