Rust / Lifetime Elision Rules
Before Rust 1.0, writing code that returns a reference without any lifetime annotation wouldn’t compile. Because Rust does not know what’s the lifetime of the returned reference:
// 🚫 would not compile
fn first_word(s: &str) -> &str { ... }
// ✅ compile
fn first_word<'a>(s: &'a str) -> &'a str {
In later versions, the Rust compiler can automatically analyze and figure out the lifetime. It is called lifetime elision and is based on a set of rules. For now, these rules are applied for the fn
and impl
blocks. In the future, more elision rules will be added.
In a function, lifetimes on the parameters are called input lifetimes, and lifetimes on the return values are called output lifetimes.
If the lifetimes are not explicitly annotated, the compiler will try to apply these three rules:
Each parameter that is a reference gets its own lifetime parameter, for example:
fn foo(first: &str) // is equivalent to fn foo<'a>(first: &'a str) fn foo(first: &str, second: &Bar) // is equivalent to fn foo<'a, 'b>(first: &'a str, second: &'b Bar)
If there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters:
fn foo(first: &str) -> &str // is equivalent to fn foo<'a>(first: &'a str) -> &'a str
If there are multiple input lifetime parameters, but one of them is
&self
or&mut self
, the lifetime ofself
will be assigned to all output lifetime parameters:fn foo(&self, first: &str) -> &str // is equivalent to fn foo<'a, 'b>(&'a self, first: &'b str) -> &'a str
If all of the above rules are satisfied, you are good to go. If the compiler fails to apply any of the above rules, it will stop and show a compile error, asking you to annotate the lifetime yourself.
For example, in the following function, there are two lifetime parameters 'a
and 'b
:
// 🚫 would not compile
fn split<'a, 'b>(source: &'a str, delimiter: &'b str) -> &??? str
In this case, the compiler could not figure out the output lifetime, so it will show a compile error.