Why async block in map not take value's ownship?

I have a simple code like this:

fn main() {
    let a = vec![1, 2, 3];
    let _b: Vec<_> = a
        .into_iter()
        .map(|val| async {
            let _ = val + 1;
        })
        .collect();
}

Then it failed due to:

error[E0373]: async block may outlive the current function, but it borrows `val`, which is owned by the current function
  --> src/main.rs:8:20
   |
8  |           .map(|val| async {
   |  ____________________^
9  | |             let _ = val + 1;
   | |                     --- `val` is borrowed here
10 | |         })
   | |_________^ may outlive borrowed value `val`
   |
note: async block is returned here
  --> src/main.rs:8:20
   |
8  |           .map(|val| async {
   |  ____________________^
9  | |             let _ = val + 1;
10 | |         })
   | |_________^
help: to force the async block to take ownership of `val` (and any other referenced variables), use the `move` keyword
   |
8  |         .map(|val| async move {
   |                          ++++

For more information about this error, try `rustc --explain E0373`.
error: could not compile `test_rust` (bin "test_rust") due to previous error

Why is val not copied here?

The answer is in the error message, you need to use an async move block:

fn main() {
    let a = vec![1, 2, 3];
    let _b: Vec<_> = a
        .into_iter()
        .map(|val| async move {
            let _ = val + 1;
        })
        .collect();
}

Playground.

The async block only captures a reference to val, because of Rust's capturing rules. Rust tries to capture only "as little as needed" to make the closure (or async block in your case) work. In order to make your block work, the compiler deemed capturing val by reference was enough. To force the capturing to move ownership, rather than capturing val by reference, you need to explicitly tell the compiler that this is what you want to do with the move statement.

1 Like

Why is val not copied here?

AFAIK val is copied when you use the move keyword. Otherwise a reference is captured, which is just how capturing works in Rust:

https://doc.rust-lang.org/reference/types/closure.html#capture-modes

If the move keyword is used, then all captures are by move or, for Copy types, by copy, regardless of whether a borrow would work. The move keyword is usually used to allow the closure to outlive the captured values, such as if the closure is being returned or used to spawn a new thread.

It is copied, but only when the async block body is executed, which is too late. Instead you want it to be copied when the async block is created, which is what move does.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.