How to return different boxed futures using impl Future?

When trying to compile this playground snippet I am getting the following error:

Compiling playground v0.0.1 (/playground)
error[E0308]: `if` and `else` have incompatible types
  --> src/main.rs:13:9
   |
10 | /     if b {
11 | |         bar().map(|v| v + 1)
   | |         -------------------- expected because of this
12 | |     } else {
13 | |         Box::pin(async { 0u32 })
   | |         ^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `futures_util::future::future::map::Map`, found struct `std::pin::Pin`
14 | |     }
   | |_____- `if` and `else` have incompatible types
   |
   = note: expected type `futures_util::future::future::map::Map<std::pin::Pin<std::boxed::Box<dyn core::future::future::Future<Output = u32> + std::marker::Send>>, [closure@src/main.rs:11:19: 11:28]>`
            found struct `std::pin::Pin<std::boxed::Box<impl core::future::future::Future>>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

Why can't the compiler infer that both return values implement the Future trait?

Even if you return impl Trait from a function, you still have to return the same type from each branch - you just don't have to name what that type is.

1 Like

Both branches must return a Boxed Pin with a type that implements Future with the same return type.

Possible solution:
I would suggest calling Box::pin on the first branch and setting's foo return type to Pin<Box<dyn Future<Item=u32>>>

fn foo(b: bool) -> Pin<Box<dyn Future<Output = u32>>> {
    if b {
        Box::pin(bar().map(|v| v + 1))
    } else {
        Box::pin(async { 0u32 })
    }
}

Because FutureA != FutureB - so a dynamic type must be returned.

Alright, got it. Would it be possible to avoid boxing or is it impossible?

You could use Either:

use futures::future::Either;

fn foo(b: bool) -> impl Future<Output = u32> {
    if b {
        Either::Left(bar().map(|v| v + 1))
    } else {
        Either::Right(async { 0u32 })
    }
}
1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.