`Box::pin(T)` vs ` boxed(self)` from `FutureExt` trait

Straight to the point:

The compiler says:

   |
10 |     fn new<Fut>(future: Fut) -> Task
   |            --- this type parameter
...
18 |         Task { future }
   |                ^^^^^^ expected trait object `dyn Future`, found type parameter `Fut`
   |
   = note: expected struct `Mutex<Pin<Box<(dyn Future<Output = ()> + Send + 'static)>>>`
              found struct `Mutex<Pin<Box<Fut>>>`
   = help: type parameters must be constrained to match other types

Unless we declare it inlined. (Sorry, I can only post two links max)

fn new<Fut>(future: Fut) -> Task
where
    Fut: Future<Output = ()> + Send + 'static,
{
    Task {
        future: std::sync::Mutex::new(Box::pin(future)),
    }
}

What's the reason behind it? And how to make the 2nd example works?

The second example fails because Box::pin does not also erase the type of the future like .boxed() does. However, type annotations to force the erasure make it succeed:

let pinboxed: Pin<Box<dyn Send + Future<Output = ()>>> = Box::pin(future);

The second one works because type inference is clever enough to see through it all and conclude the correct types.

1 Like

As a side not, a mutex around a future is a very strange and not very useful construct. Why do you want it?

1 Like

Another way to make the second example work is to insert an explicit coercion point, so that Rust knows that it should do something with the value (and this "something" happens to be unsizing); the corresponding line will look like

let pinboxed = Box::pin(future) as _;

(this _ is then filled by the compiler to be Pin<Box<dyn Send + Future<Output = ()>>>).

2 Likes

It's the example from Tokio Tutorial explaining how async runtime roughly works. I just code along. :sweat_smile:

Ah, how did I not figure it out? Thanks!

Almost got it. I tried something like...
let pinboxed = Box::pin(future) as Pin<Box<dyn Future<Output = ()> + Send>>;
...but didn't work. I must be did something wrong.

Thanks!