BoxFuture vs impl Future & lifetime

Compiling the following code (playground fails:

use std::future::Future;
use std::pin::Pin;

pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;

#[derive(Clone)]
struct A;

trait I {
    fn foo(&self, a: &A) -> BoxFuture<'static, ()>;
    fn bar(&self, a: &A) -> impl 'static + Future<Output = ()> + Send;
}

impl I for () {
    fn foo(&self, _a: &A) -> BoxFuture<'static, ()> {
        Box::pin(async move {})
    }

    fn bar(&self, _a: &A) -> impl 'static + Future<Output = ()> + Send {
        async move {}
    }
}

fn baz<F, FR>(_f: F)
where
    F: Fn(&A) -> FR,
    FR: 'static + Future<Output = ()> + Send,
{
}

fn main() {
    baz(|a| ().foo(a)); //OK
    baz(|a| ().bar(a)); //lifetime error
}

It gives the error:

error: lifetime may not live long enough
  --> src/main.rs:33:13
   |
33 |     baz(|a| ().bar(a));
   |          -- ^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
   |          ||
   |          |return type of closure `impl Future<Output = ()> + Send + 'static` contains a lifetime `'2`
   |          has type `&'1 A`

which does't make sense to me, since both foo() and bar() have 'static return types.
Does anyone have an explanation?

It's overcapturing in RPIT(IT). Solution by TAIT.
Also see full replies for this thread for more discussions/contexts that may help you.

3 Likes

Interesting, I've been having a similar issue these last few days, but with closures to be specific. I noticed if I gave it an fn item it worked, but didn't with a closure (both without boxing, and a lifetime different than 'static).

Do closures behave as if they have a RPIT return type that is always overcapturing? :thinking:

It's usually that the compiler has poor higher-ranked closure inference,[1] particularly around closures that are supposed to be higher-ranked on both input and output (i.e. take an input with any lifetime, and return something that captures that lifetime), which results in a case of undercapturing.

Closures don't directly have opaque ("RPIT") return types so far.


  1. and no good or complete way to override the inference ↩︎

2 Likes