Does &'a T imply T: 'a?
Asked Answered
D

1

10

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?

Dutiful answered 1/9, 2023 at 18:23 Comment(2)
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.Unavailing
@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.Dutiful
D
8

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.

See elsewhere in the Reference on trait and lifetime bounds:

Lifetime bounds required for types to be well-formed are sometimes inferred.

fn requires_t_outlives_a<'a, T>(x: &'a T) {}

The type parameter T is required to outlive 'a for the type &'a T to be well-formed. This is inferred because the function signature contains the type &'a T which is only valid if T: 'a holds.


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> {
  |           ++++
Dutiful answered 1/9, 2023 at 19:0 Comment(3)
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.Younglove
@SilvioMayolo I pulled that from the RFC so its definitely intentional: "In this case, the impl has two inputs [MakeRef<'a> and Vec<T>] ... 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.Dutiful
Great answer! Thanks for explaining it and sharing the relevant RFC! Quite useful.Unavailing

© 2022 - 2024 — McMap. All rights reserved.