Lifetimes
Rule
Prefer lifetime elision and owned results; use explicit lifetimes when borrowing is the purpose of a view, parser, iterator, or zero-copy abstraction.
Why
Explicit lifetimes are useful for real borrowed views but add coupling when introduced only to avoid small clones or allocations.
Do
- Rely on elision for ordinary borrowed inputs and accessors.
- Use lifetime-bearing structs for genuine views into another value.
- Name a lifetime when an output must clearly borrow from one input.
- Use
'_when the local relationship needs no name. - Return borrowed iterators when that is the natural API.
- Keep lifetime parameters out of unrelated types and layers.
Avoid
- Do not build self-referential structs in ordinary code.
- Do not name lifetimes that elision already communicates.
- Do not make public APIs lifetime-heavy unless borrowing is central.
- Do not add lifetimes solely to avoid a cheap owned result.
Public API Notes
Parsers, views, and zero-copy libraries may expose lifetimes deliberately. Ordinary APIs follow ownership policy.
Example
#![allow(unused)]
fn main() {
pub struct Token<'a> {
text: &'a str,
}
impl Token<'_> {
pub fn as_str(&self) -> &str {
self.text
}
}
pub fn first_token(input: &str) -> Option<Token<'_>> {
input
.split_whitespace()
.next()
.map(|text| Token { text })
}
}
Exceptions
- Accept more lifetime complexity in measured hot paths where allocation matters.
- Use borrowed adapter structs when their short relationship to a source is the abstraction.