如何有条件地返回不同类型的期货?



我有一个方法,根据谓词,它将返回一个或另一个未来。换句话说,返回未来的 if-else 表达式:

extern crate futures; // 0.1.23
use futures::{future, Future};
fn f() -> impl Future<Item = usize, Error = ()> {
if 1 > 0 {
future::ok(2).map(|x| x)
} else {
future::ok(10).and_then(|x| future::ok(x + 2))
}
}

这不会编译:

error[E0308]: if and else have incompatible types
--> src/lib.rs:6:5
|
6  | /     if 1 > 0 {
7  | |         future::ok(2).map(|x| x)
8  | |     } else {
9  | |         future::ok(10).and_then(|x| future::ok(x + 2))
10 | |     }
| |_____^ expected struct `futures::Map`, found struct `futures::AndThen`
|
= note: expected type `futures::Map<futures::FutureResult<{integer}, _>, [closure@src/lib.rs:7:27: 7:32]>`
found type `futures::AndThen<futures::FutureResult<{integer}, _>, futures::FutureResult<{integer}, _>, [closure@src/lib.rs:9:33: 9:54]>`

期货的创建方式不同,并且可能持有关闭,因此它们的类型不相等。理想情况下,该解决方案不会使用Boxes,因为我的异步逻辑的其余部分不使用它们。

期货中的"如果-否则"逻辑通常如何完成?

使用async/await

从 Rust 1.39 开始,您可以使用asyncawait语法来涵盖大多数情况:

async fn a() -> usize {
2
}
async fn b() -> usize {
10
}
async fn f() -> usize {
if 1 > 0 {
a().await
} else {
b().await + 2
}
}

另请参阅:

  • 在 Rust 中 async/await 的目的是什么?

Either

通过FutureExt特征使用futures::future::Either没有额外的堆分配:

use futures::{Future, FutureExt}; // 0.3.5
async fn a() -> usize {
2
}
async fn b() -> usize {
10
}
fn f() -> impl Future<Output = usize> {
if 1 > 0 {
a().left_future()
} else {
b().right_future()
}
}

但是,这需要固定的堆栈分配。如果A占用 1 个字节并且发生 99% 的时间,但B占用 512 个字节,则您的Either将始终占用 512 个字节(加上一些字节(。这并不总是一场胜利。

此解决方案也适用于Stream

装箱特征对象

这里我们使用FutureExt::boxed返回一个 trait 对象:

use futures::{Future, FutureExt}; // 0.3.5
async fn a() -> usize {
2
}
async fn b() -> usize {
10
}
fn f() -> impl Future<Output = usize> {
if 1 > 0 {
a().boxed()
} else {
b().boxed()
}
}

此解决方案也适用于Stream


正如Matthieu M.指出的那样,这两种解决方案可以结合起来:

我要指出的是,对于大B的情况,有一个中间解决方案:Either(A, Box<B>)。这样,您只需在极少数情况下为堆分配付费

,这是一种B

请注意Either,如果您有 2 个以上的条件 (Either<A, Either<B, C>>;Either<Either<A, B>, Either<C, D>>等(:

use futures::{Future, FutureExt}; // 0.3.5
async fn a() -> i32 {
2
}
async fn b() -> i32 {
0
}
async fn c() -> i32 {
-2
}
fn f(v: i32) -> impl Future<Output = i32> {
use std::cmp::Ordering;
match v.cmp(&0) {
Ordering::Less => a().left_future(),
Ordering::Equal => b().left_future().right_future(),
Ordering::Greater => c().right_future().right_future(),
}
}

另请参阅:

  • 有条件地迭代几个可能的迭代器之一

相关内容

  • 没有找到相关文章

最新更新