不能使用 `impl Future` 在向量中存储异步函数

Ken*_*eth 4 types asynchronous rust

我试图将async函数存储在向量中,但似乎impl不能在向量类型定义中使用:

use std::future::Future;

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

    v.push(haha);
}

async fn haha() {
    println!("haha");
}
Run Code Online (Sandbox Code Playgroud)
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![];
  |                            ^^^^^^^^^^^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

如何在向量中写入类型?

我发现使用类型别名可能有一个解决方法,所以我更改了代码:

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");
}
Run Code Online (Sandbox Code Playgroud)

这也不起作用;这次错误发生在类型别名中:

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 = ()>;
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

我如何解决它?

rod*_*igo 8

你不能使用impl Trait这种方式。为了能够将实现 trait 的不同类型存储到同一个容器中,您必须使用动态调度,通过存储类似Box<dyn Trait>.

在您的特定情况下,您没有指定是要存储异步函数本身还是由异步函数生成的未来,解决方案会有所不同。

要仅存储期货,您可以编写一个容器,例如:

let mut v: Vec<Box<dyn Future<Output = ()>>> = vec![];
Run Code Online (Sandbox Code Playgroud)

然后只需调用该函数,将其装箱并将其存储在容器中:

v.push(Box::new(haha()));
Run Code Online (Sandbox Code Playgroud)

相反,如果您想存储异步函数本身而不调用它,则需要一个带有 double 的容器dyn

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

现在,由于你的haha函数没有实现这个Fn特性,你需要一个适配器。一个 lambda 函数可以,但不要忘记 double Box

v2.push(Box::new(|| Box::new(haha())));
Run Code Online (Sandbox Code Playgroud)

不幸的是,使用这些解决方案,您将能够创建向量,但不能.await用于您的未来。为此,您需要期货来实现Unpin标记。这向编译器保证未来在它运行时不会移动(如果它移动了,实现将是完全不安全的)。您可以将+ Unpin要求添加到期货中,但async fn不是Unpin这样您就无法填充向量。修复它的最简单方法是使用以下方便的功能std

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

for f in v2 {
    f().into_pin().await;
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,它仍然不稳定。幸运的是,有一个From实现完全相同的实现。所以你可以写:

for f in v2 {
    Pin::from(f()).await;
}
Run Code Online (Sandbox Code Playgroud)

在下面的评论中,您编写此代码以等待期货:

for f in v2 {
    async { f().await }
}
Run Code Online (Sandbox Code Playgroud)

请注意,一个async块本身将评估另一个未来,所以在这里你只是将每个未来包装到另一个未来,但没有人在等待那个未来。实际上,您会收到有关它的警告:

警告:std::future::Future必须使用未使用的实现者。

请记住,为了正确等待所有期货,您将需要一个异步运行时。


She*_*ter 6

rodrigo 的答案是正确的,但我更喜欢使用Box::pin该类型并将其烘焙Pin到集合的 API 中。这使得使用Future特征对象(或生成特征对象的闭包特征Future对象)变得更容易:

use std::{future::Future, pin::Pin};

type PinFutureObj<Output> = Pin<Box<dyn Future<Output = Output>>>;

async fn collection_of_pinned_future_trait_objects() {
    let v: Vec<PinFutureObj<()>> = vec![
        Box::pin(haha()),
        Box::pin(hehe()),
        Box::pin(haha()),
        Box::pin(hehe()),
    ];

    for f in v {
        f.await
    }
}

async fn collection_of_closure_trait_objects() {
    let v: Vec<Box<dyn Fn() -> PinFutureObj<()>>> = vec![
        Box::new(|| Box::pin(haha())),
        Box::new(|| Box::pin(hehe())),
        Box::new(|| Box::pin(haha())),
        Box::new(|| Box::pin(hehe())),
    ];

    for f in v {
        f().await
    }
}

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

async fn hehe() {
    println!("hehe");
}
Run Code Online (Sandbox Code Playgroud)

我还将开始为较长的类型引入类型别名。

事实上,这个类型别名已经存在于 futures 箱中,并且LocalBoxFuture可以通过FutureExt::boxed_local. 还产生了添加共同特征界限BoxFuture的产品。FutureExt::boxed

use futures::future::{FutureExt, LocalBoxFuture}; // 0.3.5

async fn collection_of_pinned_future_trait_objects() {
    let v: Vec<LocalBoxFuture<'static, ()>> = vec![
        haha().boxed_local(),
        hehe().boxed_local(),
        haha().boxed_local(),
        hehe().boxed_local(),
    ];

    for f in v {
        f.await
    }
}

async fn collection_of_closure_trait_objects() {
    let v: Vec<Box<dyn Fn() -> LocalBoxFuture<'static, ()>>> = vec![
        Box::new(|| haha().boxed_local()),
        Box::new(|| hehe().boxed_local()),
        Box::new(|| haha().boxed_local()),
        Box::new(|| hehe().boxed_local()),
    ];

    for f in v {
        f().await
    }
}

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

async fn hehe() {
    println!("hehe");
}
Run Code Online (Sandbox Code Playgroud)

也可以看看: