I have a method that, depending on a predicate, will return one future or another. In other words, an if-else expression that returns a future:
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))
}
}
This doesn't compile:
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]>`
The futures are created differently, and might hold closures, so their types are not equal. Ideally, the solution wouldn't use Box
es, since the rest of my async logic doesn't use them.
How is if-else logic in futures normally done?
Either
Using futures::future::Either
has no additional heap allocation:
extern crate futures; // 0.1.23
use futures::{
future::{self, Either},
Future,
};
fn f() -> impl Future<Item = usize, Error = ()> {
if 1 > 0 {
Either::A(future::ok(2).map(|x| x))
} else {
Either::B(future::ok(10).and_then(|x| future::ok(x + 2)))
}
}
However, this requires a fixed stack allocation. If A
takes 1 byte and happens 99% of the time, but B
takes up 512 bytes, your Either
will always take up 512 bytes (plus some). This isn't always a win.
Boxed trait objects
extern crate futures; // 0.1.23
use futures::{future, Future};
fn f() -> Box<Future<Item = usize, Error = ()>> {
if 1 > 0 {
Box::new(future::ok(2).map(|x| x))
} else {
Box::new(future::ok(10).and_then(|x| future::ok(x + 2)))
}
}
As Matthieu M. points out, the two solutions can be combined:
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
Note that you can also stack Either
s if you have more than 2 conditions (Either<A, Either<B, C>>
; Either<Either<A, B>, Either<C, D>>
, etc.):
See also:
来源:https://stackoverflow.com/questions/51885745/how-do-i-conditionally-return-different-types-of-futures