I believe that this function declaration tells Rust that the lifetime of the function\'s output is the same as the lifetime of it\'s s
parameter:
fn
What does the annotation <'a> after the function name mean?
fn substr<'a>(s: &'a str, until: u32) -> &'a str;
// ^^^^
This is declaring a generic lifetime parameter. It's similar to a generic type parameter (often seen as
), in that the caller of the function gets to decide what the lifetime is. Like you said, the lifetime of the result will be the same as the lifetime of the first argument.
All lifetime names are equivalent, except for one: 'static
. This lifetime is pre-set to mean "guaranteed to live for the entire life of the program".
The most common lifetime parameter name is probably 'a
, but you can use any letter or string. Single letters are most common, but any snake_case
identifier is acceptable.
Why does the compiler need it, and what does it do with it?
Rust generally favors things to be explicit, unless there's a very good ergonomic benefit. For lifetimes, lifetime elision takes care of something like 85+% of cases, which seemed like a clear win.
Type parameters live in the same namespace as other types — is T
a generic type or did someone name a struct that? Thus type parameters need to have an explicit annotation that shows that T
is a parameter and not a real type. However, lifetime parameters don't have this same problem, so that's not the reason.
Instead, the main benefit of explicitly listing type parameters is because you can control how multiple parameters interact. A nonsense example:
fn better_str<'a, 'b, 'c>(a: &'a str, b: &'b str) -> &'c str
where
'a: 'c,
'b: 'c,
{
if a.len() < b.len() {
a
} else {
b
}
}
We have two strings and say that the input strings may have different lifetimes, but must both outlive the lifetime of the result value.
Another example, as pointed out by DK, is that structs can have their own lifetimes. I made this example also a bit of nonsense, but it hopefully conveys the point:
struct Player<'a> {
name: &'a str,
}
fn name<'p, 'n>(player: &'p Player<'n>) -> &'n str {
player.name
}
Lifetimes can be one of the more mind-bending parts of Rust, but they are pretty great when you start to grasp them.