How to store async functions?

I was working on a project and I needed to put 2 async functions with the same signature in a vec, but everything I tried didn't work. Why can I not just store for example two references to the functions instead of having to use 20 Boxes just to end up with it not compiling again?

There is a slightly hacky way to with wrapper functions:

use futures::future::Future;

use std::pin::Pin;

async fn foo() -> i32 {
    1
}

async fn bar() -> i32 {
    2
}

fn foo_wrapper() -> Pin<Box<dyn Future<Output = i32>>> {
    Box::pin(foo())
}
fn bar_wrapper() -> Pin<Box<dyn Future<Output = i32>>> {
    Box::pin(bar())
}

#[tokio::main]
async fn main() {
    let fns: Vec<&dyn Fn() -> Pin<Box<dyn Future<Output = i32>>>> =
        vec![&foo_wrapper, &bar_wrapper];

    assert_eq!((fns[0])().await, 1);
    assert_eq!((fns[1])().await, 2);
}

Playground


Edit: a more generic wrapper function:

use futures::future::{BoxFuture, Future};

async fn foo() -> i32 {
    1
}

async fn bar() -> i32 {
    2
}

fn wrapper<T, F>(f: F) -> Box<dyn Fn() -> BoxFuture<'static, i32>> 
where 
    T: Future<Output=i32> + Send + 'static,
    F: Fn() -> T + 'static,
{
    Box::new(move || Box::pin(f()))
}

#[tokio::main]
async fn main() {
    let fns: Vec<Box<dyn Fn() -> BoxFuture<'static, i32>>> =
        vec![wrapper(foo), wrapper(bar)];

    assert_eq!((fns[0])().await, 1);
    assert_eq!((fns[1])().await, 2);
}

Playground.

The most straightforward/general way is to use something like Box<dyn Fn(Arguments…) -> BoxFuture<'static, Output>>, though you might need to deal with lifetimes if the argument types are/use/contain references. Feel free to give more context to your concrete situation for more help. Also, the function will need to be converted into such a type, using a “wrapper future”, though if that’s a common need, a helper function can reduce some boilerplate to avoid the need to repeat the wrapping; feel free to ask more details on this point, too.

If what you are starting with are all function items, you could also use fn… instead of Box<dyn Fn…>.

1 Like

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.