我有一个方法,依赖于一个谓词,它将返回一个或另一个未来。换句话说,返回未来的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,因为我的异步逻辑的其余部分不使用它们。
如果其他逻辑在期货通常是怎么做的?

最佳答案

Either
使用futures::future::Either没有额外的堆分配:

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

但是,这需要固定的堆栈分配。如果A占用1个字节并且99%的时间发生,但是B占用512个字节,那么您的Either将始终占用512个字节(加上一些字节)。这并不总是一场胜利。
装箱特征对象
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)))
    }
}

由于Matthieu M. points out,这两种解决方案可以组合在一起:
我要指出,对于大型BEither(A, Box<B>)的情况,有一个中间的解决方案。这样,您只需在很少的情况下为堆分配付费,在这种情况下,堆分配是B
注意,如果有两个以上的条件(EitherEither<A, Either<B, C>>,等等),也可以堆栈Either<Either<A, B>, Either<C, D>>s:
fn f(v: i32) -> impl Future<Item = i32, Error = ()> {
    use std::cmp::Ordering;
    match v.cmp(&0) {
        Ordering::Less => Either::A(future::ok(2).map(|x| -x)),
        Ordering::Equal => Either::B(Either::A(future::ok(0))),
        Ordering::Greater => Either::B(Either::B(future::ok(-2).map(|x| x * x))),
    }
}

另见:
Conditionally iterate over one of several possible iterators

关于asynchronous - 我如何有条件地退回不同类型的 future ?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/53050460/

10-11 21:49