Peter
Peter

Reputation: 91

How to join_all from a Vec of closures that return asyncs?

I want to generate a Vec of .awaits and execute them with join_all:

use futures::future::join_all; // 0.3.5
use std::future::Future;

async fn hello(name: &str) -> String {
    format!("Hello {}!", name)
}

async fn main() {
    let urls = vec!["Peter", "Hans", "Jake"];
    let mut requests: Vec<Box<dyn Fn() -> Box<dyn Future<Output = String>>>> = vec![];
    for url in urls {
        requests.push(Box::new(|| Box::new(hello(&url))));
    }
    let responses: Vec<String> = join_all(requests).await;

    println!("Response: {:?}", responses);
}

I get the error message:

error[E0277]: `dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>` cannot be unpinned
  --> src/main.rs:15:45
   |
15 |     let responses : Vec<String>  = join_all(requests).await;
   |                                             ^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>`
   | 
  ::: /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.5/src/future/join_all.rs:83:14
   |
83 |     I::Item: Future,
   |              ------ required by this bound in `futures::future::join_all`
   |
   = note: required because of the requirements on the impl of `futures::Future` for `std::boxed::Box<dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>>`

error[E0277]: `dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>` is not a future
  --> src/main.rs:15:45
   |
15 |     let responses : Vec<String>  = join_all(requests).await;
   |                                             ^^^^^^^^ `dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>` is not a future
   | 
  ::: /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.5/src/future/join_all.rs:83:14
   |
83 |     I::Item: Future,
   |              ------ required by this bound in `futures::future::join_all`
   |
   = help: the trait `futures::Future` is not implemented for `dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>`
   = note: required because of the requirements on the impl of `futures::Future` for `std::boxed::Box<dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>>`

error[E0277]: `dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>` cannot be unpinned
  --> src/main.rs:15:36
   |
15 |     let responses : Vec<String>  = join_all(requests).await;
   |                                    ^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>`
   | 
  ::: /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.5/src/future/join_all.rs:28:8
   |
28 |     F: Future,
   |        ------ required by this bound in `futures::future::JoinAll`
   |
   = note: required because of the requirements on the impl of `futures::Future` for `std::boxed::Box<dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>>`

error[E0277]: `dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>` is not a future
  --> src/main.rs:15:36
   |
15 |     let responses : Vec<String>  = join_all(requests).await;
   |                                    ^^^^^^^^^^^^^^^^^^ `dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>` is not a future
   | 
  ::: /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.5/src/future/join_all.rs:28:8
   |
28 |     F: Future,
   |        ------ required by this bound in `futures::future::JoinAll`
   |
   = help: the trait `futures::Future` is not implemented for `dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>`
   = note: required because of the requirements on the impl of `futures::Future` for `std::boxed::Box<dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>>`

error[E0277]: `dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>` cannot be unpinned
  --> src/main.rs:15:36
   |
15 |     let responses : Vec<String>  = join_all(requests).await;
   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>`
   |
   = note: required because of the requirements on the impl of `futures::Future` for `std::boxed::Box<dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>>`

error[E0277]: `dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>` is not a future
  --> src/main.rs:15:36
   |
15 |     let responses : Vec<String>  = join_all(requests).await;
   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^ `dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>` is not a future
   |
   = help: the trait `futures::Future` is not implemented for `dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>`
   = note: required because of the requirements on the impl of `futures::Future` for `std::boxed::Box<dyn std::ops::Fn() -> std::boxed::Box<dyn futures::Future<Output = std::string::String>>>`

error[E0277]: `main` has invalid return type `impl futures::Future`
 --> src/main.rs:9:17
  |
9 | async fn main() {
  |                 ^ `main` can only return types that implement `std::process::Termination`
  |
  = help: consider using `()`, or a `Result`

error[E0752]: `main` function is not allowed to be `async`
 --> src/main.rs:9:1
  |
9 | async fn main() {
  | ^^^^^^^^^^^^^^^ `main` function is not allowed to be `async`

Upvotes: 7

Views: 3313

Answers (1)

Shepmaster
Shepmaster

Reputation: 432239

join_all requires an iterator of futures, not an iterator of functions returning futures:

pub fn join_all<I>(i: I) -> JoinAll<<I as IntoIterator>::Item>
where
    I: IntoIterator,
    <I as IntoIterator>::Item: Future,

Additionally, your futures can't be pinned and so join_all cannot use them.

The shortest fix is to:

  1. Call the closure
  2. Pin the future
use futures::future; // 0.3.5
use std::{future::Future, pin::Pin};

async fn hello(name: &str) -> String {
    format!("Hello {}!", name)
}

pub async fn example() {
    let urls = vec!["Peter", "Hans", "Jake"];
    let mut requests: Vec<Box<dyn Fn() -> Pin<Box<dyn Future<Output = String>>>>> = vec![];
    for url in urls {
        requests.push(Box::new(move || Box::pin(hello(&url))));
    }
    let responses: Vec<String> = future::join_all(requests.into_iter().map(|r| r())).await;

    println!("Response: {:?}", responses);
}

This can be written more succinctly as:

use futures::future::{self, FutureExt, LocalBoxFuture}; // 0.3.5

async fn hello(name: &str) -> String {
    format!("Hello {}!", name)
}

pub async fn example() {
    let urls = vec!["Peter", "Hans", "Jake"];
    let mut requests: Vec<Box<dyn Fn() -> LocalBoxFuture<'static, String>>> = vec![];
    for url in urls {
        requests.push(Box::new(move || hello(&url).boxed_local()));
    }
    let responses: Vec<String> = future::join_all(requests.into_iter().map(|r| r())).await;

    println!("Response: {:?}", responses);
}

However, none of that is needed for your specific example:

  1. The closure doesn't provide any value
  2. You have one type of future so there's no need for dynamic dispatch
  3. You don't even need a Vec
use futures::future; // 0.3.5

async fn hello(name: &str) -> String {
    format!("Hello {}!", name)
}

pub async fn example() {
    let urls = vec!["Peter", "Hans", "Jake"];

    let hellos = urls.iter().map(|u| hello(u));
    let responses = future::join_all(hellos).await;

    println!("Response: {:?}", responses);
}

See also:

Upvotes: 3

Related Questions