How can I store an async function in a struct and call it from a struct instance?

Let’s use this as our Minimal, Reproducible Example:

async fn foo(x: u8) -> u8 {
    2 * x
}

struct S {
    foo: (),
}

async fn example() {
    let s = S { foo };
}

It produces the error:

error[E0308]: mismatched types
  --> src/main.rs:10:17
   |
10 |     let s = S { foo };
   |                 ^^^ expected (), found fn item
   |
   = note: expected type `()`
              found type `fn(u8) -> impl std::future::Future {foo}`

The type of foo is a function pointer that takes a u8 and returns some type implementing the trait std::future::Future. async fn is effectively just syntax sugar that transforms -> Foo into -> impl Future<Output = Foo>.

We make our struct generic and place a trait bound on the generic that matches. In real code, you’d probably want to place a constraint on the the Output associated type, but it’s not needed for this example. We can then call the function like any other callable member field:

async fn foo(x: u8) -> u8 {
    2 * x
}

struct S<F>
where
    F: std::future::Future,
{
    foo: fn(u8) -> F,
}

impl<F> S<F>
where
    F: std::future::Future,
{
    async fn do_thing(self) {
        (self.foo)(42).await;
    }
}

async fn example() {
    let s = S { foo };
    s.do_thing().await;
}

To be even more flexible, you could use another generic to store a closure, instead of forcing only a function pointer:

struct S<C, F>
where
    C: Fn(u8) -> F,
    F: std::future::Future,
{
    foo: C,
}

impl<C, F> S<C, F>
where
    C: Fn(u8) -> F,
    F: std::future::Future,
{
    async fn do_thing(self) {
        (self.foo)(42).await;
    }
}

See also:

Leave a Comment