Implied Lifetime Constraint on Generic

trait Foo<T> {
    fn bar(&self, t: &mut T);
}

fn baz<'a, T>(foo: impl Foo<T> + 'a) {}

Does the baz function here imply a lifetime constraint of T: 'a?

If so, how can I change Foo or baz to avoid (or relax) this constraint as it isn't a type that's stored or dropped when Foo is dropped?

It doesn’t. To demonstrate:

trait Foo<T> {
    fn bar(&self, t: &mut T);
}

fn baz<'a, T>(foo: impl Foo<T> + 'a) {}

impl<T> Foo<T> for () {
    fn bar(&self, t: &mut T) {}
}

// test without `T: 'static` bound
fn test<T>() {
    baz::<'static, T>(());
}

Do note that in practice some your types implementing Foo might come with a comparable restriction though. E.g. if you implement impl<T> Foo<T> for SomeStruct<T>, then SomeStruct<T> can only be used for impl Foo<T> + 'a if the bound SomeStruct<T>: 'a and hence T: 'a is implemented.

A similar thing could apply to trait objects. E.g. if you have a generic impl lifting Foo<T> implementations from S: ?Sized to Box<S>, and then try to pass Box<dyn Foo<T>>[1] for the impl Foo<T> + 'a, then a Box<dyn Foo<T>>: 'a requirement would also imply a T: 'a requirement.


  1. or Box<dyn Foo<T> + 'b> ↩︎

1 Like

No. The + 'a is a (explicit) bound on the implementing type, not on T.

1 Like

E.g. if you implement impl<T> Foo<T> for SomeStruct<T> , then SomeStruct<T> can only be used for impl Foo<T> + 'a if the bound SomeStruct<T>: 'a and hence T: 'a is implemented

Can you explain this case a bit more? This applies even while SomeStruct does not store a T?

Can I do something to prevent the constraint from applying to T if the struct doesn't store one?

Your function can be rewritten like so:

fn baz<'a, T, F: Foo<T> + 'a>(foo: F) {}

And the bounds in question are:

F: Foo<T>,
F: 'a

Which, given F = SomeStruct<T>, are:

SomeStruct<T>: Foo<T>,
SomeStruct<T>: 'a

Outlives bounds like SomeStruct<T>: 'a are defined syntactically, and in this case, requires T: 'a.

No, it's required syntactically and there's not an override.


The lifetime is meaningless in your example so this may be an XY problem.

Why must the lifetime constraint apply to T if SomeStruct only contains a fn(T)?

If no T will be dropped, it seems like the constraint is applied for no reason?

I am using closures and function pointers quite a bit so I am trying to understand the relationship between the lifetime constraints and generics in that case.

It use to work more like that (10 years ago), but was unsound and buggy. This is the relevant RFC.

https://rust-lang.github.io/rfcs/1214-projections-lifetimes-and-wf.html

For a concrete example, if fn(Cell<&'a str>) was 'static, you could cast it to dyn Any and then downcast it to... what? It can't soundly be fn(Cell<&'b str>) for a different lifetime 'b, but there isn't a finite set of lifetimes.

1 Like

Here's an issue for function pointers specifically.

1 Like