How to execute multiple async fn's at once and use join_all to get all their results

i tried tokio with tasks but nothing is working can anyone tell me what is wrong with this code

fn main() {
    block_on(speak());
}

async fn speak() {
    let hold = vec![say(),greet()];
    let results = join_all(hold).await;
}

async fn say(){
    println!("hello");
}

async fn greet(){
    println!("world");
}

here is the compiler output

error[E0308]: mismatched types
--> sync\src\main.rs:14:27
|
14 | let hold = vec![say(),greet()];
| ^^^^^^^ expected opaque type, found a different opaque type
...
23 | async fn greet(){
| - the Output of this async fn's found opaque type
|
= note: expected type impl core::future::future::Future (opaque type at <sync\src\main.rs:19:15>)
found opaque type impl core::future::future::Future (opaque type at <sync\src\main.rs:23:17>)
= note: distinct uses of impl Trait result in different opaque types

error: aborting due to previous error; 3 warnings emitted

For more information about this error, try rustc --explain E0308.
error: could not compile sync.

The two functions say and greet return two different types (two different futures), so you cannot store them in the same vector directly, as a vector may only contain a single type of future.

To get around this, you can box them: (in this case the type annotation is necessary)

use futures::future::BoxFuture;
use futures::future::join_all;

async fn speak() {
    let hold: Vec<BoxFuture<_>> = vec![Box::pin(say()), Box::pin(greet())];
    let results = join_all(hold).await;
}

Or you can use the join! macro: (only works with a fixed number of futures)

async fn speak() {
    let results = tokio::join!(say(), greet());
}

Since there are only two of them, you can also use Either, which is cheaper than boxing them. Unlike join!, this allows having the same future many times (but still only two different types).

use futures::future::Either;
use futures::future::join_all;

async fn speak() {
    let hold = vec![Either::Left(say()), Either::Right(greet())];
    let results = join_all(hold).await;
}

Finally, you can also spawn them:

async fn speak() {
    let hold = vec![
        tokio::spawn(say()),
        tokio::spawn(greet()),
    ];
    for handle in hold {
        handle.await.expect("Panic in spawned task");
    }
}

In this case the tokio::spawn function makes the future start running immediately, so you don't also need join_all.

4 Likes

well this was super use full thank you so much you are a savior.

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.