How can one await a result of a boxed future?
Asked Answered
D

2

22
use futures::{future, Future};

fn test() -> Box<dyn Future<Output = bool>> {
    Box::new(future::ok::<bool>(true))
}

async fn async_fn() -> bool {
    let result: bool = test().await;
    return result;
}

fn main(){
    async_fn();
    println!("Hello!");
}

Playground

Error:

error[E0277]: the trait bound `dyn core::future::future::Future<Output = bool>: std::marker::Unpin` is not satisfied
  --> src/main.rs:8:24
   |
8  |     let result: bool = test().await;
   |                        ^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `dyn core::future::future::Future<Output = bool>`
   |
   = note: required because of the requirements on the impl of `core::future::future::Future` for `std::boxed::Box<dyn core::future::future::Future<Output = bool>>`
Dragone answered 6/3, 2020 at 9:54 Comment(1)
just listen the compiler play.rust-lang.org/…Jute
A
27

According to the implementation:

impl<F> Future for Box<F>
where
    F: Unpin + Future + ?Sized, 

Boxed futures only implement the Future trait when the future inside the Box implements Unpin.

Since your function doesn't guarantee that the returned future implements Unpin, your return value will be considered to not implement Future. You'll not able to await it because your type is basically not a Future.

The solution from @Stargateur, adding an explicit type boundary to the signature, works (Playground):

fn test() -> Box<dyn Future<Output = Result<bool, ()>> + Unpin> 

If you are using futures-rs, there is a helper type BoxFuture. You can use BoxedFuture without explicitly stating Unpin:

use futures::future::BoxFuture;

fn test() -> BoxFuture<'static, Result<bool, ()>> {
    Box::pin(async { Ok(true) })
}

Playground

Ataman answered 6/3, 2020 at 11:4 Comment(1)
Thanks a lot as well! Helped me a lot get a better understanding and I like the hint with the futures crate helper.Dragone
P
19

When it comes to Box and future, it almost always make sense to use Box::pin instead of Box::new:

use std::pin::Pin;
use futures::{future, Future};

fn test() -> Pin<Box<dyn Future<Output = Result<bool, ()>>>> {
    Box::pin(future::ok(true))
}

async fn async_fn() -> bool {
    test().await.unwrap()
}

The reason is quite interesting. Pin has a blanket implementation for Unpin:

impl<P> Unpin for Pin<P> where
    P: Unpin,

And the Box<T> inside it is unconditionally Unpin:

impl<T> Unpin for Box<T> where
    T: ?Sized,

So a Pin<Box<dyn Future>> is a unpinned Future. Everything works out, but why Box itself doesn't? This is one place where Deref gets in the way:

impl<T: ?Sized> Deref for Box<T> {
    type Target = T;
}

await expects an unpinned Future, and the Box<dyn Future> you created with Box::new does contain a Future. So it is auto-dereferenced and the Unpin is lost unless you explicitly state it that way with Box<dyn Future + Unpin>.


Edit: @ÖmerErden is right about why Box<dyn Future> wouldn't work.

Photograph answered 6/3, 2020 at 10:37 Comment(2)
Thanks for the explanation regarding the Unpin + Box. That helped a lot!Dragone
Side Note: You can await on Box<dyn Future>, By creating Pin wrapper, Example: Pin::from(Box::new(async {})).awaitFlatboat

© 2022 - 2024 — McMap. All rights reserved.