tezvyn:

Rust's Lifetime Elision: When You Can Skip 'a

AI-drafted, machine-checkedSource: doc.rust-lang.orgintermediate

Lifetime elision lets you omit explicit lifetimes (`'a`) in function signatures. The compiler infers them from common patterns, like a function taking one reference and returning one.

WHY IT EXISTS Manually annotating every lifetime for common, obvious borrowing patterns is tedious and clutters code. Lifetime elision was introduced to make idiomatic Rust more ergonomic by allowing the compiler to infer lifetimes in these frequent scenarios, reducing boilerplate without sacrificing safety.

THE MENTAL MODEL Think of lifetime elision as compiler-provided shortcuts for the most common borrowing scenarios. Instead of you telling the compiler "the output reference lives as long as this input reference," the compiler assumes it based on a few simple rules. If your function's borrowing pattern doesn't fit one of these rules, you must write the lifetimes out explicitly.

HOW IT WORKS The compiler applies three rules to function signatures to infer elided lifetimes. First, each elided lifetime in an input parameter becomes a distinct lifetime parameter. Second, if there is exactly one input lifetime (elided or not), that lifetime is assigned to all elided output lifetimes. Third, if one of the parameters is &self or &mut self, the lifetime of self is assigned to all elided output lifetimes. If these rules don't resolve all lifetimes, the compiler reports an error.

WHEN TO USE IT You use elision constantly, often without thinking about it. It's designed for the most common function signatures. For example, a function that takes a slice and returns a sub-slice, like fn first_three(s: &[i32]) -> &[i32], relies on elision. Similarly, a method on a struct that returns a reference to one of its own fields, like fn get_name(&self) -> &String, uses the &self elision rule.

WHEN NOT TO USE IT You cannot use elision when the lifetime relationships are ambiguous. The classic case is a function that takes two string slices and returns one, like fn longest(x: &str, y: &str) -> &str. The compiler doesn't know if the returned slice should be tied to x's lifetime or y's. You must be explicit: fn longest<'a>(x: &'a str, y: &'a str) -> &'a str. It also fails for functions that return a reference without taking any reference inputs.

ONE CANONICAL EXAMPLE A function to find the first word in a string, fn first_word(s: &str) -> &str, is a perfect example. It uses lifetime elision. Because there is only one input reference, the compiler applies the second rule and expands the signature to fn first_word<'a>(s: &'a str) -> &'a str behind the scenes. This correctly enforces that the returned string slice cannot outlive the input string slice it was derived from.

Read the original → doc.rust-lang.org

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.