How do I conditionally return different types of futures?

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

See also:

Either

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

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.

This solution also works for Streams.

Boxed trait objects

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

This solution also works for Streams.


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

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

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

See also:

Leave a Comment