LibraryLifetime Elision Rules

Lifetime Elision Rules

Learn about Lifetime Elision Rules as part of Rust Systems Programming

Understanding Rust's Lifetime Elision Rules

In Rust, lifetimes are a compile-time concept that ensures references are always valid. While you can explicitly annotate lifetimes, Rust's compiler is smart enough to infer many of them using a set of rules known as Lifetime Elision. Mastering these rules is crucial for writing efficient and safe Rust code without excessive annotation.

Why Lifetime Elision?

Explicitly annotating every lifetime can make code verbose and harder to read. Lifetime elision allows the compiler to automatically determine the lifetimes of references in many common scenarios, reducing boilerplate and improving code clarity. This feature is a cornerstone of Rust's ergonomics.

The Three Lifetime Elision Rules

The Rust compiler applies three specific rules to elide lifetimes in function signatures. If these rules don't provide enough information, you'll need to add explicit lifetime annotations.

Rule 1: Each elided lifetime in a function corresponds to one input lifetime that the compiler can determine.

In functions with a single input reference parameter, that parameter's lifetime is assigned to any output reference. This is the simplest case.

What is the primary goal of Rust's lifetime elision rules?

To automatically infer lifetimes in common scenarios, reducing boilerplate and improving code readability.

Rule 2: If there are multiple input lifetime parameters, but one of them is `&self` or `&mut self`, the lifetime of `&self` is assigned to all elided output lifetimes.

This rule applies to methods. If a method takes

code
&self
and returns a reference, the returned reference's lifetime will be the same as
code
&self
.

Rule 3: If there are multiple input lifetime parameters, but none of them are `&self` or `&mut self`, the compiler will error.

This rule is a catch-all. If the first two rules don't apply and there are multiple input lifetimes, the compiler cannot unambiguously determine which input lifetime should apply to the output. In such cases, you must explicitly annotate the lifetimes.

Illustrative Examples

Let's look at how these rules apply in practice.

Example 1: Single Input Reference

Consider a function that returns a reference to the longest of two string slices. Without explicit lifetimes, Rust infers the lifetime.

fn longest(x: &str, y: &str) -> &str { if x.len() > y.len() { x } else { y } }

This function signature is equivalent to:

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } }

Rule 1 applies here: there's one input reference parameter (implicitly, as there are two, but the rule is about the number of distinct input lifetimes that can be inferred for output). Since there are two input references, and the output is a reference, the compiler assumes the output reference's lifetime is the same as the input references. If the input references had different lifetimes, the compiler would need more information. However, in this specific case, the compiler can infer that the returned reference must live at least as long as the shorter of the two input references. The compiler effectively assigns a single lifetime parameter 'a to both input references and the output reference.

📚

Text-based content

Library pages focus on text content

Example 2: Method with `&self`

Imagine a struct with a method that returns a reference to one of its fields.

rust
struct ImportantExcerpt<'a> {
part: &'a str,
}
impl<'a> ImportantExcerpt<'a> {
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("Attention all news!");
self.part
}
}

In

code
announce_and_return_part
, there are two input references:
code
&self
and
code
announcement: &str
. Rule 2 states that if
code
&self
is present, its lifetime is assigned to any elided output lifetimes. Therefore, the returned
code
&str
(which is
code
self.part
) will have the same lifetime as
code
&self
.

Example 3: Multiple Input Lifetimes Without `&self`

Consider a function that takes two string slices and returns a reference to the longer one, but without

code
&self
.

rust
// This code will NOT compile without explicit lifetimes
// fn longest_no_self(x: &str, y: &str) -> &str {
// if x.len() > y.len() {
// x
// } else {
// y
// }
// }

This function signature would require explicit lifetime annotations because Rule 3 applies: there are multiple input lifetimes (

code
'a
for
code
x
and
code
'b
for
code
y
), and no
code
&self
parameter. The compiler cannot determine whether the output reference should borrow from
code
x
or
code
y
.

When in doubt, or when the compiler complains about ambiguous lifetimes, explicitly annotate them. This makes your code's lifetime requirements clear and helps prevent subtle bugs.

Summary of Elision Rules

ScenarioRule AppliedOutput Lifetime
Function with one input referenceRule 1Same as the input reference
Method with &self or &mut selfRule 2Same as &self or &mut self
Function with multiple input references (no &self)Rule 3 (Error)Requires explicit annotation

Understanding these rules allows you to write more concise and idiomatic Rust code. They are designed to cover the most common patterns of borrowing and returning references.

Learning Resources

The Rust Programming Language: Lifetimes(documentation)

The official Rust book provides a comprehensive explanation of lifetimes, including elision rules with clear examples.

Rust Lifetime Elision Rules Explained(video)

A visual and auditory explanation of Rust's lifetime elision rules, breaking down each rule with code demonstrations.

Rust's Lifetime Elision Rules: A Deep Dive(blog)

This blog post offers a detailed walkthrough of the lifetime elision rules, providing practical insights and common pitfalls.

Rust Programming - Lifetimes(video)

Another excellent video tutorial that covers Rust lifetimes, with a specific focus on how elision simplifies code.

Rust by Example: Lifetimes(documentation)

Rust by Example offers interactive code snippets demonstrating various lifetime concepts, including elision in practice.

Understanding Rust Lifetimes(video)

A clear explanation of Rust's lifetime system, touching upon the importance and application of elision rules.

Rust Programming Language - Chapter 10: Generic Types, Traits, and Lifetimes(documentation)

The full chapter on generics, traits, and lifetimes in the Rust book, providing context for elision rules.

Rust Lifetimes: The Complete Guide(blog)

A beginner-friendly guide to Rust lifetimes, explaining the core concepts and how elision helps.

Rust Programming - Lifetimes and Borrowing(video)

A video that delves into Rust's borrowing and lifetime system, highlighting the benefits of lifetime elision.

Rust Programming Language - Lifetime Syntax(documentation)

A direct link to the section on lifetime syntax within the official Rust book, specifically detailing elision.