Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot use `impl Future` to store async function in a vector

I am trying to store async functions in a vector, but it seems like impl cannot be used in the vector type definition:

use std::future::Future;

fn main() {
    let mut v: Vec<fn() -> impl Future<Output = ()>> = vec![];

    v.push(haha);
}

async fn haha() {
    println!("haha");
}
error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
 --> src/main.rs:4:28
  |
4 |     let mut v: Vec<fn() -> impl Future<Output = ()>> = vec![];
  |                            ^^^^^^^^^^^^^^^^^^^^^^^^

How do I write the type inside the vector?

I found that there may be a workaround by using a type alias, so I changed the code:

use std::future::Future;

type Haha = impl Future<Output = ()>;

fn main() {
    let mut v: Vec<fn() -> Haha> = vec![];

    v.push(haha);
}

async fn haha() {
    println!("haha");
}

This doesn't work either; this time the error occurs in the type alias:

error[E0658]: `impl Trait` in type aliases is unstable
 --> src/main.rs:3:1
  |
3 | type Haha = impl Future<Output = ()>;
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: for more information, see https://github.com/rust-lang/rust/issues/63063

error[E0308]: mismatched types
 --> src/main.rs:8:12
  |
8 |     v.push(haha);
  |            ^^^^ expected opaque type, found a different opaque type
  |
  = note: expected type `fn() -> Haha`
             found type `fn() -> impl std::future::Future {haha}`
  = note: distinct uses of `impl Trait` result in different opaque types

error: could not find defining uses
 --> src/main.rs:3:1
  |
3 | type Haha = impl Future<Output = ()>;
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

How do I fix it?

like image 467
Kenneth Avatar asked Dec 18 '22 15:12

Kenneth


1 Answers

You cannot use the impl Trait this way. To be able to store different types that implement a trait into the same container you have to use dynamic dispatch, by storing something like Box<dyn Trait>.

In your particular case, you do not specify if you want to store the async functions themselves or the future generated by the async functions, the solution would be somewhat different.

To store just the futures, you write a container such as:

let mut v: Vec<Box<dyn Future<Output = ()>>> = vec![];

And then just call the function, box it and store it in the container:

v.push(Box::new(haha()));

If instead you want to store the async function itself, without calling it, you need a container with a double dyn:

let mut v2: Vec<Box<dyn Fn() -> Box<dyn Future<Output = ()>>>> = vec![];

Now, since your haha function does not implement this Fn trait you need an adaptor. A lambda function will do, but don't forget the double Box:

v2.push(Box::new(|| Box::new(haha())));

Unfortunately, with these solutions you will be able to create the vector, but not to .await for your futures. For that you need the futures to implement the Unpin marker. That guarantees to the compiler that the future will not move while it is running (if it did, the implementation would be totally unsafe). You could add the + Unpin requirement to the futures, but async fn are not Unpin so you could not fill the vector. The easiest way to fix it is to use this handy function from std:

pub fn into_pin(boxed: Box<T>) -> Pin<Box<T>>

for f in v2 {
    f().into_pin().await;
}

Unfortunately, it is still unstable. Fortunately, there is a From impl that does exactly the same. So you can just write:

for f in v2 {
    Pin::from(f()).await;
}

In your comment below you write this code to wait for the futures:

for f in v2 {
    async { f().await }
}

Note that an async block itself will evaluate to another future, so here you are just wrapping each future into another future, but nobody is waiting for that one. Actually you'll get a warning about it:

warning: unused implementer of std::future::Future that must be used.

Remember that in order to properly wait for all the futures you will need an async runtime.

like image 83
rodrigo Avatar answered May 24 '23 20:05

rodrigo