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

£可爱£侵袭症+ 提交于 2020-06-07 04:41:09

问题


I'm trying to achieve this with the new async/await syntax, std::future::Futures and a recent version of Tokio. I'm using Tokio 0.2.0-alpha.4 and Rust 1.39.0-nightly.

Different things I've tried include:

  • using Box<dyn>s for the types that I want to store in the struct
  • using generics in the struct definition

I couldn't quite get a minimal working version, so here's a simplified version of what I'm trying to achieve:

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

// type StorableAsyncFn = Fn(u8) -> dyn Future<Output = u8>;

struct S {
    f: StorableAsyncFn,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let s = S { f: foo };

    let out = (s.f)(1).await;

    Ok(())
}

Of course this code fails to compile with the following error:

error[E0412]: cannot find type `StorableAsyncFn` in this scope

StorableAsyncFn is not defined here, it's the type I'm trying to define.


回答1:


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:

  • How do I call a function through a member variable?
  • How do I store a closure in a struct in Rust?


来源:https://stackoverflow.com/questions/58173711/how-can-i-store-an-async-function-in-a-struct-and-call-it-from-a-struct-instance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!