Lifetimes in async function and iter.map(|..| async{...})

I'm curious as to why this results in the "async block may outlive the current function".

It seems to me that the "await" call on the Vec<_> collected from the FuturesOrdered ensures any futures constructed by the map wont outlive the function?

(I'm not so concerned about adding "move" to solve this, its inconvenient as I will need to clone some other data that will need to make into the async block but not a show stopper)

use futures::StreamExt;

#[derive(Debug)]
struct Data{
    val: usize,
    name: String
}

struct DataGroup{
    names: Vec<Data>
}


impl DataGroup{
    async fn return_future(self) -> Vec<usize>{
        self.names.iter().map(|n| async {
            println!(" {:?}", n);
            0
        }).collect::<futures::stream::FuturesOrdered<_>>().collect::<Vec<_>>().await
    }
}


fn main() {
    let mut dg = DataGroup{names: vec![]};
    for i in 0..10{
        dg.names.push(Data{val: i, name:format!("elem {i}")});
    }
    let _vals = futures::executor::block_on ( dg.return_future());
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0373]: async block may outlive the current function, but it borrows `n`, which is owned by the current function
  --> src/main.rs:16:35
   |
16 |           self.names.iter().map(|n| async {
   |  ___________________________________^
17 | |             println!(" {:?}", n);
   | |                               - `n` is borrowed here
18 | |             0
19 | |         }).collect::<futures::stream::FuturesOrdered<_>>().collect::<Vec<_>>().await
   | |_________^ may outlive borrowed value `n`
   |
note: async block is returned here
  --> src/main.rs:16:35
   |
16 |           self.names.iter().map(|n| async {
   |  ___________________________________^
17 | |             println!(" {:?}", n);
18 | |             0
19 | |         }).collect::<futures::stream::FuturesOrdered<_>>().collect::<Vec<_>>().await
   | |_________^
help: to force the async block to take ownership of `n` (and any other referenced variables), use the `move` keyword
   |
16 |         self.names.iter().map(|n| async move {
   |                                         ++++

For more information about this error, try `rustc --explain E0373`.
error: could not compile `playground` due to previous error

I don't think this is true? It's enough to dereference the data.

This is because formatting macros borrow their arguments unconditionally, so when you pass n to println!(), the async block will end up borrowing n itself.

But you called .iter() to get an iterator, which means that n is already a reference, so by passing *n to println!(), you will actually end up with something like &*n, which borrows from the collection, not from a local variable in the function.


As to why borrowing a local variable is not allowed: an async block/function/closure is translated to a state machine inside a Future impl. Your async function does not actually return the collected Vec; when you call it, you get back a future that contains some state necessary for stepping the state machine whenever the next .await happens. This await may happen long after any other functions generating the initial state of the future returned.

Ahhh! Great explanation, that clears things up for me! Thanks!

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.