7

From my own understanding and experimentation this appears to be true, but I have yet to find an authoritative source that documents it. Rust by Example has a bounds section where it says:

T: 'a: All references in T must outlive lifetime 'a.

#[derive(Debug)]
struct Ref<'a, T: 'a>(&'a T);
// `Ref` contains a reference to a generic type `T` that has
// an unknown lifetime `'a`. `T` is bounded such that any
// *references* in `T` must outlive `'a`. Additionally, the lifetime
// of `Ref` may not exceed `'a`.

However, this looks like a poor demonstration since the T: 'a bound does not seem to affect the behavior of Ref. Any attempt I've made to construct a T that is shorter than 'a is thwarted with or without T: 'a. Even more, a generic reference defined without the lifetime bound can be passed off as one with it:

fn f<'a, T>(r: &'a T) {
    g(r) // this compiles
}

fn g<'a, T: 'a>(r: &'a T) {
    // ...
}

The Rust Reference has a similar construction from some examples in the generic parameters section (struct Ref<'a, T> where T: 'a { r: &'a T }), however it does not elaborate. I have looked through the documentation there, those on references, and on lifetimes and cannot find a link.

So does &'a T imply T: 'a? If so, where is this documented? And why do these resources have this unnecessary constraint? If not, what are the rules?

kmdreko
  • 42,554
  • 6
  • 57
  • 106
  • Thanks for starting this topic. It's definitely useful for developers with mid-level Rust experience. I've already spent almost a week reading about this and still things are not clear. I'm unable to come up with a type (for `T`) which does not compile if `T: 'a` constraint isn't used in Ref type you quoted. – Nawaz Sep 01 '23 at 21:42
  • 1
    @Nawaz From my answer below, you will find there is no such `T` because the `T: 'a` bound exists regardless due to the usage of `&'a T`. So there is no difference with or without writing it explicitly. – kmdreko Sep 01 '23 at 23:01

1 Answers1

6

Yes, &'a T does imply T: 'a.

There was always the requirement that referent had to outlive the referenced lifetime since it is necessary for safety by-construction. However, before Rust 1.31 the bound was not inferred and would have to be provided explicitly as shown in these questions.

It was RFC #2093: infer outlives that let the compiler to infer these bounds and thus allowed the user to elide them. Since then, the Rust by Example and Rust Reference snippets are over-specified and the T: 'a is no longer needed.


There is at least one place where &'a T does not automatically infer T: 'a and that is when defining associated types for a trait (demo on the playground) but the compiler will guide you into adding it explicitly:

trait MakeRef<'a> {
    type Type;
}

impl<'a, T> MakeRef<'a> for Vec<T> {
    type Type = &'a T;
}
error[E0309]: the parameter type `T` may not live long enough
 --> src/lib.rs:6:17
  |
6 |     type Type = &'a T;
  |                 ^^^^^ ...so that the reference type `&'a T` does not outlive the data it points at
  |
help: consider adding an explicit lifetime bound...
  |
5 | impl<'a, T: 'a> MakeRef<'a> for Vec<T> {
  |           ++++
kmdreko
  • 42,554
  • 6
  • 57
  • 106
  • Great answer! Is the lack of inference on associated types a bug or intended? If it's the latter, I wonder why it has to be that way. – Silvio Mayolo Sep 01 '23 at 21:10
  • 1
    @SilvioMayolo I pulled that [from the RFC](https://github.com/rust-lang/rfcs/blob/master/text/2093-infer-outlives.md#where-explicit-annotations-would-still-be-required) so its definitely intentional: *"In this case, the impl has two inputs [`MakeRef<'a>` and `Vec`] ... neither of these inputs requires that `T: 'a`"* though I'm not sure if there's a design reason behind preferring it that way or if it is just a technical limitation. – kmdreko Sep 01 '23 at 21:16
  • Great answer! Thanks for explaining it and sharing the relevant RFC! Quite useful. – Nawaz Sep 02 '23 at 13:14