Lifetimes for type params, “the parameter type may not live long enough”

I have code like this (this is how #[async_trait] is expanded):

trait BoolTrait {
    fn check<'s: 'f, 'v: 'f, 'f>(&'s self, value: &'v i32) -> Pin<Box<dyn Future<Output = bool> + 'f>>;
}

Actually it says that Future ('f) can contain refs to self ('s) and v ('v) , so lifetime of 's and 'v must be longer than Future 'f lifetime.

Next, I want to make any closure with async body implement this trait:

impl<F, Fut> BoolTrait for F
    where
        F: Fn(&i32) -> Fut,
        Fut: Future<Output = bool> {
    fn check<'s: 'f, 'v: 'f, 'f>(&'s self, value: &'v i32) -> Pin<Box<dyn Future<Output=bool> + 'f>> {
        Box::pin(self(value))
    }
}

But this doesn't work:

error[E0309]: the parameter type `Fut` may not live long enough
   |
16 | impl<F, Fut> BoolTrait for F
   |         --- help: consider adding an explicit lifetime bound...: `Fut: 'f`
...
21 |         Box::pin(self(value))
   |         ^^^^^^^^^^^^^^^^^^^^^ ...so that the type `Fut` will meet its required lifetime bounds

Okay, let's try 'static lifetime, just to make it compile (actually I think that my problem is here, but I don't know what correct lifetime should I use):

Fut: Future<Output = bool> + 'static

Let's check how it works:

async fn test<BT: BoolTrait>(bt: BT) {
    let v = 42;
    bt.check(&v).await;
}

fn main() {
    block_on(test(|v: &i32| async { *v == 42 }));
}
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/lib.rs:13:39
   |
13 |         block_on(test(|v: &i32| async { *v == 42 }));
   |                                       ^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 13:23...
  --> src/lib.rs:13:23
   |
13 |         block_on(test(|v: &i32| async { *v == 42 }));
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that the types are compatible
  --> src/lib.rs:13:39
   |
13 |         block_on(test(|v: &i32| async { *v == 42 }));
   |                                       ^^^^^^^^^^^^
   = note: expected `&&i32`
              found `&&i32`
   = note: but, the lifetime must be valid for the static lifetime...
note: ...so that the type `impl futures::Future` will meet its required lifetime bounds
  --> src/lib.rs:13:18
   |
13 |         block_on(test(|v: &i32| async { *v == 42 }));
   |                  ^^^^

And this is where I am. Actually, I can fix it, by replacing value: &i32 param with value: i32 , but it's not I want, I want to use exactly reference.

If I correctly understand the error, the compiler says that Future lives shorter than ref to i32, because it contains ref to it, but in the trait impl I set 'static .

It seems to me, that I need to specify somehow that the lifetime of Fut must be shorter than the lifetime &i32 , in impl .

First of all, if you're working with immutable/shared lifetimes, you can rely on the compiler to unify them for you, so you don't need 3 of them:

fn check<'a>(&'a self, value: &'a i32) -> Pin<Box<dyn Future<Output = bool> + 'a>>;

Even if the input lifetimes are actually different for self and value, compiler can just shorten them as needed for shared borrows (mutable/exclusive are stricter).


The problem you have is that you declare lifetime in the check method, but you have Fut bound outside of that method. The Fut bound therefore is unable to declare that it works for the check method.

You either need to move Fut bound to the method, or move the lifetime to the trait, so that you can declare Fut: Future<Output = bool> + 'f that exactly matches the return type.

There's no point using 'static anywhere here, because 'static forbids all temporary borrows, and in practice means references are forbidden.

1 Like

Can't you change the trait?

trait BoolTrait<'a> {
    fn check(&'a self, value: &i32) -> Pin<Box<dyn Future<Output = bool> + 'a>>;
}

impl<'a, F, Fut: 'a> BoolTrait<'a> for F
    where
        F: Fn(&i32) -> Fut,
        Fut: Future<Output = bool> + 'a {
    fn check(&'a self, value: &i32) -> Pin<Box<dyn Future<Output=bool> + 'a>> {
        Box::pin(self(value))
    }
}
1 Like

Thank you for your answer!

Okay, I tried to lift 'f lifetime on the trait level:

pub trait BoolTrait<'f> {
    fn check(&'f self, value: &'f i32) -> Pin<Box<dyn Future<Output = bool> + 'f>>;
}
impl<'f, F, Fut> BoolTrait<'f> for F
    where
        F: Fn(&i32) -> Fut,
        Fut: Future<Output = bool> + 'f {
    fn check(&'f self, value: &'f i32) -> Pin<Box<dyn Future<Output=bool> + 'f>> {
        Box::pin(self(value))
    }
}

But now I have problems with lifetimes in the test method:

    async fn test<'a, BT: BoolTrait<'a> + 'a>(bt: BT) {
        let v = 42;
        bt.check(&v).await;
    }

Produces next error:

error[E0597]: `bt` does not live long enough
   |
7  |     async fn test<'a, BT: BoolTrait<'a> + 'a>(bt: BT) {
   |                   -- lifetime `'a` defined here
8  |         let v = 42;
9  |         bt.check(&v).await;
   |         ^^----------
   |         |
   |         borrowed value does not live long enough
   |         argument requires that `bt` is borrowed for `'a`
10 |     }
   |     - `bt` dropped here while still borrowed

error[E0597]: `v` does not live long enough
   |
7  |     async fn test<'a, BT: BoolTrait<'a> + 'a>(bt: BT) {
   |                   -- lifetime `'a` defined here
8  |         let v = 42;
9  |         bt.check(&v).await;
   |         ---------^^-
   |         |        |
   |         |        borrowed value does not live long enough
   |         argument requires that `v` is borrowed for `'a`
10 |     }
   |     - `v` dropped here while still borrowed

I think that 'a (lifetime for BoolTrait) should equal lifetime of test function body: I'm using the returned Future only inside of this function, so I don't need it more. But still can't get how to do this.

Sorry to send you back and forth on this. Lifetimes in generic code are exponentially harder than anything else in Rust, because not only your code has to satisfy them in practice, it also has to express correct bounds in all possible hypothetical cases.

async fn test<'a, BT: BoolTrait<'a> + 'a>(bt: BT) {
        let v = 42;
        bt.check(&v).await;
    }

In this case 'a is an argument to the function, so it means the reference had to already exist before test<'a> was called. This means you can't use a local variable and take the reference later, because lifetime can't go back in time to start existing before bt.

This should work:

async fn test<'a, BT: BoolTrait<'a> + 'a>(bt: BT, v: &'a i32) {
    bt.check(v).await;
}

or there's an obscure for<'a> syntax that allows you to use a type with arbitrary lifetime:

async fn test<BT>(bt: BT) where BT: for<'a> BoolTrait<'a> {
    let v = 42;
    bt.check(&v).await;
}

You likely want the for<'lt> syntax, also known as higher-rank trait bounds, but it is certainly an advanced feature that takes some time to wrap your head around.

While you're here, any chance of rustc suggesting for<'a> in the case lifetime comes from a local variable, rather than an argument?

1 Like

Performing this suggestion would be difficult, but likely worthwhile. We are already suggesting for for some cases, but those are easier to detect the intended usage because it is in definition and at that point we can know what can be done.

For this case in particular we need to identify that the type of the borrowed value is a type parameter, look at its trait bounds and figure out that it references the lifetime requirement and for<'lt> could have been used. It is certainly possible to enable rustc to do this, it will just be hard.

As a general when the implementation and the definition disagree, we defer heavily towards the definition as the source of truth. Doing that in this case would lead us astray.

Would you mind filing a ticket so we don't forget to tackle this at some point in the future?

1 Like

Thanks! Looks interesting, I didn't see this construction before, just tried this approach and get the next error:

24 |       block_on(test(|v: &i32| async { *v == 42 }));
   |                ^^^^ implementation of `BoolTrait` is not general enough

I have created a playground to make it clearer
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=ff97ae3ec0b4d0c72a9cab310f8bf9f2

It seems to refer to this issue https://github.com/rust-lang/rust/issues/70263

1 Like

Ah yeah, I've seen that ticket before. As of yet I don't think there's a way to make this work in this way.