无法使用“ Impl Future”将异步函数存储在向量中

时间:2019-10-12 13:26:05

标签: asynchronous types 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");
}
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![];
  |                            ^^^^^^^^^^^^^^^^^^^^^^^^

如何在向量中写入类型?

我发现使用类型别名可能有解决方法,因此我更改了代码:

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");
}

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

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 = ()>;
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

我该如何解决?

2 个答案:

答案 0 :(得分:2)

您不能以这种方式使用impl Trait。为了能够将实现特征的不同类型存储到同一容器中,必须通过存储Box<dyn Trait>之类的东西来使用动态调度。

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

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

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

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

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

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

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

现在,由于您的haha函数无法实现此Fn特征,因此您需要一个适配器。 Lambda函数可以,但是不要忘记双Box

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

不幸的是,使用这些解决方案,您将能够创建向量,但对于期货而言,则无法.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;
}

不幸的是,它仍然不稳定。幸运的是,有一个From隐含功能完全相同。所以你可以这样写:

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

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

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

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

  

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

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

答案 1 :(得分:0)

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

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

type PinFutureObj<Output> = Pin<Box<dyn Future<Output = Output>>>;
// Or use the existing alias:
// use futures::future::LocalBoxFuture; // 0.3.1

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");
}

我还将开始为较长的类型引入类型别名。此类型别名已经以LocalBoxFuture

的形式存在于期货周转箱中

另请参阅: