如何将 futures 0.1 迭代器收集到向量中?

How do I collect a futures 0.1 iterator into a vector?

我正在尝试使用 Rust 1.36.0 和 futures 0.1 构建 futures 向量。

  1. 为什么下面的代码不起作用?
  2. 是否有更惯用的方法来构建 Future 的 list/iterable?
extern crate futures;

pub fn create_some_futures() {
    let mapped: Vec<Box<dyn futures::future::Future<Item = i32, Error = ()>>> = (0..10)
        .map(|_| Box::new(futures::future::ok(132)))
        .collect();
}

fn main() {
    create_some_futures();
}

我的Cargo.toml:

[dependencies]
futures = "0.1"

这不编译:

error[E0277]: a collection of type `std::vec::Vec<std::boxed::Box<dyn futures::Future<Item = i32, Error = ()>>>` cannot be built from an iterator over elements of type `std::boxed::Box<futures::Failed<{integer}, _>>`
 --> src/main.rs:6:10
  |
6 |         .collect();
  |          ^^^^^^^ a collection of type `std::vec::Vec<std::boxed::Box<dyn futures::Future<Item = i32, Error = ()>>>` cannot be built from `std::iter::Iterator<Item=std::boxed::Box<futures::Failed<{integer}, _>>>`
  |
  = help: the trait `std::iter::FromIterator<std::boxed::Box<futures::Failed<{integer}, _>>>` is not implemented for `std::vec::Vec<std::boxed::Box<dyn futures::Future<Item = i32, Error = ()>>>`

这是为什么?我怀疑将 futures::Finished<{integer}, _> 转换为 <dyn futures::Future<Item = i32, Error = ()>> 有问题,因为这样编译很好:

pub fn create_some_boxed_ints() {
    let mapped: Vec<Box<i32>> = (0..10).map(|_| Box::new(132)).collect();
}

我不确定是什么问题。函数 future::ok returns FutureResult 实现了 Future,我希望它与 dyn Future<Item = i32, Error = ()>.

兼容

我正在玩这个旧版本的 futures crate,因为我想贡献的另一个项目正在使用 0.1 版本。我知道 Future 的关联类型是 0.3.x 中的 Output。如果我切换到更新的版本,也许我不会有这个问题,但我想了解上面的案例以更好地理解 Rust。错误消息在 1.39.0 上是相同的。

为什么下面的代码不起作用?

我不认为这个问题特定于您使用的期货版本 - 如果我更新您的代码以使用 Future<Output = Result<i32, i32>> 然后我得到 exactly the same result.

这里的问题是您的映射函数需要将具体的未来强制转换为特征对象。

在 Rust 参考中,在 type coercions 部分中,它描述了强制转换站点:

places where the desired type is explicit or can be derived by propagation from explicit types (without type inference).

您的代码需要强制转换的上下文将需要类型推断 - 从所需的容器类型通过 map 函数的 return 类型向后工作。桥太远了。

您可以通过显式转换为特征对象来克服它:

Box::new(futures::future::ok(132)) as Box<dyn futures::future::Future<Output = Result<i32, i32>>>

现在它将编译 (playground)

为避免隐式转换,您可以将 return 类型添加到 map 闭包中:

let mapped: Vec<Box<dyn futures::future::Future<Output = Result<i32, i32>>>> = (0..10)
    .map(|_| -> Box<dyn futures::future::Future<Output = Result<i32, i32>>> {
        Box::new(futures::future::ok(132))
    })
    .collect();

这将允许隐式强制生效。

是否有更惯用的方法来构建 Future 的 list/iterable?

这将取决于期货的创建方式以及它们的用途。

  • 你可以定义一个类型别名:

    type MyDynFuture = Box<dyn futures::future::Future<Output = Result<i32, i32>>>;
    

    这样你就可以写:

    let mapped: Vec<MyDynFuture> = (0..10)
        .map(|_| -> MyDynFuture {
            Box::new(futures::future::ok(132))
        })
        .collect();
    

    我个人觉得更易读。

  • 如果你的代码库中有很多地方你需要 管理期货的集合,你可能想使用一个函数 而不是关闭以进一步减少样板文件。

  • 如果集合将包含一组有限的未来类型,那么它可能是 更有效地定义一个包含它们的枚举并收集 容器中此枚举的实例 - 这将避免运行时 分派和堆分配。

  • 如果期货是从某个迭代器生成的,并且 集合将被传递给 join_all 或类似的 方法,这样你就可以等待它们全部完成,那么你就不需要 收集它们 - 例如:

    let i = (0..10)
        .map(|n| -> futures::future::Ready<Result<i32,i32>> {
            futures::future::ok(n)
        });
    
    join_all(i)
    

警告:我在 Rust 方面也很缺乏经验,可能还有其他我不知道的最佳实践。