Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
854 views
in Technique[技术] by (71.8m points)

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

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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:


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...