Async / await in iterator closures

Hi!
I'm struggling to call an async function from a closure on an iterator - e.g. in a call to map. Here is a minimal contrived example that fails to compile. I'd like to end up with a collection (e.g. a Vec) after awaiting each of the async calls.
Am I missing something simple to make this work?

#![feature(await_macro, async_await, futures_api)]


async fn do_something(num: i32) -> i32 {
    num + 1
}

async fn runner() {
    
    let results = (0..5).map(|i| do_something(i) )
    .map(|future| await! { future } )
    .collect::<Vec<i32>>();
    
    println!("Got {:?}", results);
}

(Playground)

Errors:

    Compiling playground v0.0.1 (/playground)
error[E0628]: generators cannot have explicit arguments
  --> src/lib.rs:11:10
   |
11 |     .map(|future| await! { future } )
   |          ^^^^^^^^

The normal way I would deal with the error is to wrap it in an async block - but then would have to explicitly poll the future manually. Can this be achieved with the await! syntax?
(Edited to make the actual issue more clear)

1 Like

This can't work. You can use await only in async functions, but the closure is another different function and its not async, so you can't await in there.

Right. Thanks @kornel.
Is there an idomatic way to achieve the same goal?
i.e. I have an async function and I need to call it with each item from a collection, and collect the results.

Using futures-preview you can create an iterator of futures then collect them into a FuturesOrdered and collect the results back into a vector, something like

use futures::stream::{FuturesOrdered, StreamExt};

async fn runner() {
    let results = await!((0..5).map(|i| do_something(i)).collect::<FuturesOrdered<_>>().collect::<Vec<_>>());
    println!("Got {:?}", results);
}

or use join_all to run them all to completion

use futures::future;

async fn runner() {
    let results = await!(future::join_all((0..5).map(|i| do_something(i))));
    println!("Got {:?}", results);
}

Depending on what sort of async operation you're doing and how many elements there are one or the other will be more efficient, but they should both work.

3 Likes

Ah thanks @Nemo157! That is exactly what I was looking for.
This also indirectly helped me discover the rename dependencies feature in cargo, as I had a bit of a muddle trying to use futures 0.1 (for some tokio stuff) and futures-preview together.
All solved, thanks.