For now, this reference is a best-effort document. We strive for validity and completeness, but are not yet there. In the future, the docs and lang teams will work together to figure out how best to do this. Until then, this is a best-effort attempt. If you find something wrong or missing, file an issue or send in a pull request.

Lifetime elision

Rust has rules that allow lifetimes to be elided in various places where the compiler can infer a sensible default choice.

Lifetime elision in functions

In order to make common patterns more ergonomic, Rust allows lifetimes to be elided in function item, function pointer and closure trait signatures. The following rules are used to infer lifetime parameters for elided lifetimes. It is an error to elide lifetime parameters that cannot be inferred.

  • Each elided lifetime in the parameters becomes a distinct lifetime parameter.
  • If there is exactly one lifetime used in the parameters (elided or not), that lifetime is assigned to all elided output lifetimes.

In method signatures there is another rule

  • If the receiver has type &Self or &mut Self, then the lifetime of that reference to Self is assigned to all elided output lifetime parameters.

Examples:

fn print(s: &str); // elided fn print<'a>(s: &'a str); // expanded fn debug(lvl: usize, s: &str); // elided fn debug<'a>(lvl: usize, s: &'a str); // expanded fn substr(s: &str, until: usize) -> &str; // elided fn substr<'a>(s: &'a str, until: usize) -> &'a str; // expanded fn get_str() -> &str; // ILLEGAL fn frob(s: &str, t: &str) -> &str; // ILLEGAL fn get_mut(&mut self) -> &mut T; // elided fn get_mut<'a>(&'a mut self) -> &'a mut T; // expanded fn args<T: ToCStr>(&mut self, args: &[T]) -> &mut Command; // elided fn args<'a, 'b, T: ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command; // expanded fn new(buf: &mut [u8]) -> BufWriter; // elided fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a>; // expanded type FunPtr = fn(&str) -> &str; // elided type FunPtr = for<'a> fn(&'a str) -> &'a str; // expanded type FunTrait = Fn(&str) -> &str; // elided type FunTrait = for<'a> Fn(&'a str) -> &'a str; // expanded

Default trait object lifetimes

The assumed lifetime of references held by a trait object is called its default object lifetime bound. These were defined in RFC 599 and amended in RFC 1156. Default object lifetime bounds are used instead of the lifetime parameter elision rules defined above.

If the trait object is used as a type argument of a generic type then the containing type is first used to try to infer a bound.

  • If there is a unique bound from the containing type then that is the default
  • If there is more than one bound from the containing type then an explicit bound must be specified

If neither of those rules apply, then the bounds on the trait are used:

  • If the trait is defined with a single lifetime bound then that bound is used.
  • If 'static is used for any lifetime bound then 'static is used.
  • If the trait has no lifetime bounds, then the lifetime is inferred in expressions and is 'static outside of expressions.
// For the following trait... trait Foo { } // These two are the same as Box<T> has no lifetime bound on T Box<Foo> Box<Foo + 'static> // ...and so are these: impl Foo {} impl Foo + 'static {} // ...so are these, because &'a T requires T: 'a &'a Foo &'a (Foo + 'a) // std::cell::Ref<'a, T> also requires T: 'a, so these are the same std::cell::Ref<'a, Foo> std::cell::Ref<'a, Foo + 'a> // This is an error: struct TwoBounds<'a, 'b, T: ?Sized + 'a + 'b> TwoBounds<'a, 'b, Foo> // Error: the lifetime bound for this object type cannot // be deduced from context

Note that the innermost object sets the bound, so &'a Box<Foo> is still &'a Box<Foo + 'static>.

// For the following trait... trait Bar<'a>: 'a { } // ...these two are the same: Box<Bar<'a>> Box<Bar<'a> + 'a> // ...and so are these: impl<'a> Foo<'a> {} impl<'a> Foo<'a> + 'a {} // This is still an error: struct TwoBounds<'a, 'b, T: ?Sized + 'a + 'b> TwoBounds<'a, 'b, Foo<'c>>

'static lifetime elision

Both constant and static declarations of reference types have implicit 'static lifetimes unless an explicit lifetime is specified. As such, the constant declarations involving 'static above may be written without the lifetimes.

// STRING: &'static str const STRING: &str = "bitstring"; struct BitsNStrings<'a> { mybits: [u32; 2], mystring: &'a str, } // BITS_N_STRINGS: BitsNStrings<'static> const BITS_N_STRINGS: BitsNStrings = BitsNStrings { mybits: [1, 2], mystring: STRING, };

Note that if the static or const items include function or closure references, which themselves include references, the compiler will first try the standard elision rules. If it is unable to resolve the lifetimes by its usual rules, then it will error. By way of example:

// Resolved as `fn<'a>(&'a str) -> &'a str`. const RESOLVED_SINGLE: fn(&str) -> &str = .. // Resolved as `Fn<'a, 'b, 'c>(&'a Foo, &'b Bar, &'c Baz) -> usize`. const RESOLVED_MULTIPLE: &Fn(&Foo, &Bar, &Baz) -> usize = .. // There is insufficient information to bound the return reference lifetime // relative to the argument lifetimes, so this is an error. const RESOLVED_STATIC: &Fn(&Foo, &Bar) -> &Baz = ..