我如何有条件地 return 不同类型的期货?
How do I conditionally return different types of futures?
我有一种方法,根据谓词,return 一个未来或另一个。换句话说,return 是未来的 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]>`
future 的创建方式不同,并且可能会关闭,因此它们的类型不相同。理想情况下,该解决方案不会使用 Box
es,因为我的其余异步逻辑不使用它们。
futures 中的 if-else 逻辑通常是如何完成的?
使用async
/await
从 Rust 1.39 开始,您可以使用 async
和 await
语法来涵盖大多数情况:
async fn a() -> usize {
2
}
async fn b() -> usize {
10
}
async fn f() -> usize {
if 1 > 0 {
a().await
} else {
b().await + 2
}
}
另请参阅:
Either
使用 futures::future::Either
via the FutureExt
特性没有额外的堆分配:
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
s。
盒装特征对象
这里我们使用FutureExt::boxed
来return一个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
s。
如,两种方案可以结合使用:
I would note that there is a middle ground solution for the case of a large B
: Either(A, Box<B>)
. This way, you only pay for the heap allocation on the rare case where it's a B
请注意,如果您有超过 2 个条件(Either<A, Either<B, C>>
;Either<Either<A, B>, Either<C, D>>
,等等),您也可以堆叠 Either
s:
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(),
}
}
另请参阅:
我有一种方法,根据谓词,return 一个未来或另一个。换句话说,return 是未来的 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]>`
future 的创建方式不同,并且可能会关闭,因此它们的类型不相同。理想情况下,该解决方案不会使用 Box
es,因为我的其余异步逻辑不使用它们。
futures 中的 if-else 逻辑通常是如何完成的?
使用async
/await
从 Rust 1.39 开始,您可以使用 async
和 await
语法来涵盖大多数情况:
async fn a() -> usize {
2
}
async fn b() -> usize {
10
}
async fn f() -> usize {
if 1 > 0 {
a().await
} else {
b().await + 2
}
}
另请参阅:
Either
使用 futures::future::Either
via the FutureExt
特性没有额外的堆分配:
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
s。
盒装特征对象
这里我们使用FutureExt::boxed
来return一个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
s。
如
I would note that there is a middle ground solution for the case of a large
B
:Either(A, Box<B>)
. This way, you only pay for the heap allocation on the rare case where it's aB
请注意,如果您有超过 2 个条件(Either<A, Either<B, C>>
;Either<Either<A, B>, Either<C, D>>
,等等),您也可以堆叠 Either
s:
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(),
}
}
另请参阅: