如何有条件地返回不同类型的期货? [英] How do I conditionally return different types of futures?

查看:29
本文介绍了如何有条件地返回不同类型的期货?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个方法,它会根据谓词返回一个或另一个未来.换句话说,一个返回未来的 if-else 表达式:

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

这不会编译:

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,因为我的其余异步逻辑不使用它们.

The futures are created differently, and might hold closures, so their types are not equal. Ideally, the solution wouldn't use Boxes, since the rest of my async logic doesn't use them.

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

How is if-else logic in futures normally done?

推荐答案

Using async/await

从 Rust 1.39 开始,您可以使用 asyncawait 语法来覆盖大多数情况:

Using async/await

Since Rust 1.39, you can use async and await syntax to cover most cases:

async fn a() -> usize {
    2
}
async fn b() -> usize {
    10
}

async fn f() -> usize {
    if 1 > 0 {
        a().await
    } else {
        b().await + 2
    }
}

另见:

使用futures::future::Either 通过 FutureExt trait 没有额外的堆分配:

Using futures::future::Either via the FutureExt trait has no additional heap allocation:

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总是em> 占用 512 字节(加上一些).这并不总是胜利.

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.

此解决方案也适用于 Streams.

This solution also works for Streams.

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

Here we use FutureExt::boxed to return a trait object:

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

此解决方案也适用于 Streams.

This solution also works for Streams.

As Matthieu M. 指出,这两种解决方案可以结合起来:

As Matthieu M. points out, the two solutions can be combined:

我会注意到对于大型 B 的情况有一个中间解决方案:Either(A, Box).这样,您只需为 B

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 个以上的条件(EitherEither<;要么,要么,等等):

Note that you can also stack Eithers if you have more than 2 conditions (Either<A, Either<B, C>>; Either<Either<A, B>, Either<C, D>>, etc.):

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

另见:

这篇关于如何有条件地返回不同类型的期货?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆