54

Let's consider the following examples:

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"

I'm asking about the expression |x| async move {} instead of async move |x| {}. The latter is more obvious, but it runs into the compilation error:

error[E0658]: async closures are unstable

Then I wonder, what is the difference between async move || {} and || async move {}. They both seems to be closures for using the move keyword.

$ rustc --version
rustc 1.39.0 (4560ea788 2019-11-04)
unegare
  • 2,197
  • 1
  • 11
  • 25

2 Answers2

47

One is the async block (a closure with async block as its body to be precise), while the other is async closure. Per async/await RFC:

async || closures

In addition to functions, async can also be applied to closures. Like an async function, an async closure has a return type of impl Future<Output = T>, rather than T.

On the other hand:

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.

The move keyword here is to denote that the async closure and block are to capture ownership of the variables they close over.

And apparently, async closure is still deemed to be unstable. It has this tracking issue.

Community
  • 1
  • 1
edwardw
  • 12,652
  • 3
  • 40
  • 51
  • 1
    So there is no difference in its import right now, isn't it? – unegare Dec 04 '19 at 06:58
  • @dronte7 nope, apart from the fact one is unstable. – edwardw Dec 04 '19 at 07:07
  • they both are immediately turns into a Future with ot without acquiring of some surround variables. except for being unstable async closure is the same as async block with acquiring of external variables, isn't it? – unegare Dec 04 '19 at 07:15
  • @dronte7 they both return a Future *when* being called. As far as capturing variables, they are the same as well. That's what closure stands for, async or not. – edwardw Dec 04 '19 at 07:18
  • 16
    I think capturing variables is quite different in both cases. `async move || ...` will move variables from the enclosing block into the closure, while `|| async move {...}` will move variables from the closure into the async block. if you want to move them from the enclosing block into the async block, I reckon you need to use `move || async move {...}` for now. – Sven Marnach Dec 04 '19 at 08:37
  • @SvenMarnach at first i was thinking the same but it doesn't work with that way, please [check this](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=5df35babf7e698fcc0a9baca955bd991). You don't need to add extra move to the plain closure literal which evaluates async block. It should use the reference of `global_string` in the example but it just moves. – Ömer Erden Dec 04 '19 at 12:03
  • @ÖmerErden This is rather surprising. I'll look into thhis once I've got a bit of time. – Sven Marnach Dec 04 '19 at 12:59
  • @ÖmerErden I'm very confused now. [See this](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=39d4788895e68db6c43a893d9e8e8856) which supports @SvenMarnach 's opinion, you need to add `move` before `||`. – sify Feb 22 '21 at 07:19
  • @sify the difference is, in your code you are moving the owner of the reference, not the owner of the vec. – Ömer Erden Feb 23 '21 at 06:53
  • @ÖmerErden So in my case the variables are copied rather than moved. Then why can't I use a single move to achieve this effect? – sify Mar 09 '21 at 10:01
  • @sify you can, I've just shared an example that points `async move || ...` and `|| async move {...}` both captures from the same scope. – Ömer Erden Mar 09 '21 at 14:42
  • @ÖmerErden I checked and `async move || ...` works on nightly channel with `async_closure` feature, but `|| async move {...}` doesn't work. Your example uses `|| async move {...}`, you don't need `async_closure` feature and it works on stable channel. But for me `|| async move {...}` doesn't works, the variables `v` and `i` are referenced by the closure but not copied, it must be `move || async move{...}` – sify Mar 10 '21 at 02:21
  • 1
    The code within `async` block does not start till I `await` the future, right? – Mr.Wang from Next Door Apr 01 '21 at 11:04
  • This accepted answer is unsatisfying as it fails to address: Why do we have two subtlety different ways to declare async closure-like things? – gatoWololo Apr 27 '23 at 19:31
0

As to some previous comments here, there appear to be various "issues" pertaining to the correct syntax.

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

should be exactly equivalent to

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

since a guarded closure apparently cannot be declared async outside the guard, and declaring it async inside the guard simply opens up another block of code, omitting the braces. The second move moves all affected variables -- already moved into the closure -- into the async "safe space" so it would seem that all possible protections of async move are applied here.