asynchronous - 如何有条件地退还不同类型的 future ?

标签 asynchronous rust future control-flow

我有一种方法,它会根据谓词返回一个 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]>`

futures 的创建方式不同,并且可能包含闭包,因此它们的类型不相同。理想情况下,该解决方案不会使用 Boxes,因为我的其余异步逻辑不使用它们。

futures 中的 if-else 逻辑通常是如何完成的?

最佳答案

使用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

使用 futures::future::Either 通过 FutureExt 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().left_future()
    } else {
        b().right_future()
    }
}

但是,这需要固定的堆栈分配。如果A占用 1 个字节并在 99% 的时间内发生,但是 B占用 512 个字节,你的 Either总是占用 512 字节(加上一些)。这并不总是胜利。

此解决方案也适用于 Stream

盒装特征对象

这里我们使用 FutureExt::boxed 返回特征对象:

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 ,这两种解决方案可以结合起来:

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

请注意,您还可以堆叠 Either s 如果你有超过 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(),
    }
}

另见:

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

相关文章:

Java异步订单

c# - 如何运行可能同步或可能异步的代码?

rust 在匹配表达式的右侧从字符串中创建 b"string"

rust - 使用Rust宏简化 `match`

rust - PathBuf 生命周期不够长

asynchronous - 如何有条件地退还不同类型的 future ?

c# - 异步 I/O 密集型代码比非异步代码运行速度慢,为什么?

reactjs - React Formik onSubmit Async 调用了两次

c++ - 即使调用 get() , future 也不会运行

scala - 如何控制scala中future.sequence的并发?