我有一个方法,根据谓词,它将返回一个future 或另一个future .换句话说,返回future 的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]>`

期货的创建方式不同,可能包含闭包,因此它们的类型不同.理想情况下,解决方案不会使用BoxE,因为我的异步逻辑的其余部分不使用它们.

期货中的if-else逻辑通常是如何实现的?

推荐答案

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

另见:

Either

通过FutureExt trait使用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秒.

Boxed trait objects

这里我们使用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. points out,这两种解决方案可以结合使用:

我想指出的是,对于B:Either(A, Box<B>)的大样本,有一个中间解决方案.这样,您只需在罕见的情况下支付堆分配,即B

注意,如果你有Either:Either<A, Either<B, C>>的条件,也可以有Either: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(),
    }
}

另见:

Rust相关问答推荐

为什么Tauri要修改被调用函数的参数名称?

Rust,polars CSV:有没有一种方法可以从impll BufRead(或任何字节迭代器)中读取CSV?

为什么拥有的trait对象的相等运算符移动了正确的操作数?

为什么迭代器上的`. map(...)`的返回类型如此复杂?

编译项目期间使用Cargo生成时出现rustc错误

重写Rust中的方法以使用`&;mut self`而不是`mut self`

为什么我们需要std::thread::scope,如果我们可以使用thread.join()在函数的生命周期内删除引用?

如何从ruust中的fig.toml中读取?

在生存期内将非静态可变引用转换为范围内的静态可变引用

允许 rust 迹 struct 条目具有多种类型

可选包装枚举的反序列化

为什么 Rust 字符串没有短字符串优化 (SSO)?

如何将 &[T] 或 Vec<T> 转换为 Arc<Mutex<[T]>>?

一旦令牌作为文字使用,声明宏不匹配硬编码值?

仅当函数写为闭包时才会出现生命周期错误

为什么 File::read_to_end 缓冲区容量越大越慢?

将原始可变指针传递给 C FFI 后出现意外值

为什么指定生命周期让我返回一个引用?

发生移动是因为 `data` 的类型为 `Vec`,它没有实现 `Copy` 特性

覆盖类型的要求到底是什么?为什么单个元素元组满足它?