【问题标题】:How can future.then() return a Future?future.then() 如何返回 Future?
【发布时间】:2021-02-18 06:16:33
【问题描述】:

下面的函数,取自here

fn connection_for(
    &self,
    pool_key: PoolKey,
) -> impl Future<Output = Result<Pooled<PoolClient<B>>, ClientError<B>>> {

    let checkout = self.pool.checkout(pool_key.clone());
    let connect = self.connect_to(pool_key);

    let executor = self.conn_builder.exec.clone();
    // The order of the `select` is depended on below...
    future::select(checkout, connect).then(move |either| match either {
         ...

应该返回一个Future。但是,它返回的返回结果是

    future::select(checkout, connect).then(...)

.then 在哪里这样做:

fn then<Fut, F>(self, f: F) -> Then<Self, Fut, F>

这不是Future。这怎么可能?

我试图了解此函数返回的内容。这是'.then'的结尾:

       Either::Right((Err(err), checkout)) => Either::Right(Either::Right({
            if err.is_canceled() {
                Either::Left(checkout.map_err(ClientError::Normal))
            } else {
                Either::Right(future::err(ClientError::Normal(err)))
            }
        })),

看起来它返回了 Either::Right(Either::Right 的东西。我很困惑。

【问题讨论】:

  • 我很困惑,Then is a Future
  • @kmdreko 好的,所以.then(f: F) 接受f: ()-&gt;Future。这是否会使 ` Either::Right(Either::Right({...` 被类型推断转换为未来?
  • Either is a Future if its A and B are。看起来他们正在使用Eithers 将所有可能运行的期货(总共看起来像 6 个)组合成一个类型。
  • @kmdreko 好的,所以Either 确实是Future,但它怎么可能是Result&lt;Pooled&lt;PoolClient&lt;B&gt;&gt;, ClientError&lt;B&gt;&gt; 的未来呢?这是Future 所需的类型。最外面的Either是未来,但里面又是Eithers
  • @kmdreko 我的意思是,为什么它返回 Either::Right(Either::Right(Either::left(error))) 而不是返回 Future(error)

标签: rust rust-tokio tokio


【解决方案1】:

.then() 用于将两个期货链接在一起。它返回一个Then&lt;Fut1, Fut2, F&gt;,它是一个Future,它执行以下工作:轮询第一个future,使用给定函数处理结果,并轮询生成的future。


Either 类型旨在将具有相同关联输出的两个不同期货组合成一个类型。假设您正在创建一个函数,该函数将根据输入返回两个不同的期货:

async fn do_a() -> () {}
async fn do_b() -> () {}

fn do_something(cond: bool) -> impl Future<Output = ()> {
    if cond {
        do_a()
    }
    else {
        do_b()
    }
}

This won't compile 因为do_a()do_b() 返回的Futures 是不同的类型。 Rust 编译器非常友好地建议使用 Box&lt;dyn Future&gt; 动态返回不同的类型,但是出于性能原因,有时不需要额外的间接性并且没有必要。

上面可以实现为使用Either返回一个具体的Future

use std::future::Future;
use futures::future::Either;

async fn do_a() -> () {}
async fn do_b() -> () {}

fn do_something(cond: bool) -> impl Future<Output = ()> {
    if cond {
        Either::Left(do_a())
    }
    else {
        Either::Right(do_b())
    }
}

有问题的链接代码看起来有点粗糙,因为它不仅要协调两个不同的未来,还要协调五个。但是您可以毫无问题地嵌套Eithers,因为它们本身就是Futures。它更像这样:

use std::future::Future;
use futures::future::Either;

async fn do_a() -> () {}
async fn do_b() -> () {}
async fn do_c() -> () {}
async fn do_d() -> () {}
async fn do_e() -> () {}

fn do_something(cond1: bool, cond2: bool, cond3: bool) -> impl Future<Output = ()> {
    if cond1 {
        Either::Left(do_a())
    }
    else if cond2 {
        if cond3 {
            Either::Right(Either::Left(Either::Left(do_b())))
        }
        else {
            Either::Right(Either::Left(Either::Right(do_c())))
        }
    }
    else {
        if cond3 {
            Either::Right(Either::Right(Either::Left(do_d())))
        }
        else {
            Either::Right(Either::Right(Either::Right(do_e())))
        }
    }
}

这一切最终都会创建一个impl Future&lt;Output = Result&lt;Pooled&lt;PoolClient&lt;B&gt;&gt;, ClientError&lt;B&gt;&gt;&gt;,因为它可能产生的所有单个期货都返回一个Result&lt;Pooled&lt;PoolClient&lt;B&gt;&gt;, ClientError&lt;B&gt;&gt;

【讨论】:

  • 为什么 do_a() 和 do_b() 返回不同的东西?似乎相等。另外,如果我只是对do_x 的结果感兴趣,如果我可以像您在cond1 上那样做一个,为什么要将它嵌套在很多Either::LeftEither::Right 中?
  • 没有两个async 函数返回相同的类型,它们都是唯一的。这与闭包相同,只是它们设计方式的一部分。您不希望任意更改可能使两种类型相同或不同。 Either 只能有两种类型,因此要获得更多类型,您必须嵌套它们。将Lefts 和Rights 视为构建一棵树,以便每个唯一的Left/Right 路径指向不同的类型。
猜你喜欢
  • 1970-01-01
  • 2021-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-01
  • 1970-01-01
相关资源
最近更新 更多