Gatonito
Gatonito

Reputation: 2174

How can future.then() return a Future?

The function below, taken from 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 {
         ...

should return a Future. However, it returns the return result of

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

Where .then does this:

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

Which is not a Future. How it this possible?

I'm trying to understand what is returned by this function. This is the end of the '.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)))
            }
        })),

It looks like it returns Either::Right(Either::Right of something. I'm confused.

Upvotes: 3

Views: 1225

Answers (1)

kmdreko
kmdreko

Reputation: 60367

.then() is used for chaining two futures together. It returns a Then<Fut1, Fut2, F>, which is a Future that does the work of: polling the first future, processing the result with given function, and polling the resulting future.


The Either type is designed to combine two different futures having the same associated output into a single type. Say you were making a function that will return two different futures depending on the input:

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 because the Futures returned by do_a() and do_b() are different types. The Rust compiler is kind enough to suggest using Box<dyn Future> to return different types dynamically, however the extra indirection is sometimes not desired for performance reasons and is not necessary.

The above can be implemented to return a concrete Future using Either.

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

The linked code in question looks a bit gnarly because it has to not only reconcile two different futures, but five. But you can nest Eithers without a problem, since they themselves are Futures. Its more like this:

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

It all ends up creating a impl Future<Output = Result<Pooled<PoolClient<B>>, ClientError<B>>> because all the individual futures it can potentially yield each return a Result<Pooled<PoolClient<B>>, ClientError<B>>.

Upvotes: 3

Related Questions