Every reference in Rust has a lifetime, which is the scope for which that reference is valid.
Most of the time, lifetimes are implicit and inferred. The borrow checker was able to figure all out for us without any help.
But sometimes we must annotate lifetimes when the lifetimes of references could be related in a few different ways.
// [E0106] missing lifetime specifier
// this function's return type contains a borrowed value,
// but the signature does not say whether it is borrowed from `x` or `y`
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
println!("The longest string is {}", result);
}
- As the borrow checker, how would you know that
result
is not a dangling reference ? What's the lifetime ofresult
. - THe
longest
function return a reference-> &str
, but we have no idea what the lifetime of this reference is.- first of all, we return either
x
ory
, x could have a different lifttime than y. - second of all, we don't know the exact lifetime of
x
ory
because these are just placeholders.- image that this function gets called from many different places, so x and y could have many different lifetimes.
- first of all, we return either
- the borrow checker doesn't know how to handle this ambiguity, and so we get an error.
To solve this error, we need something called generic lifetime annotation.
Generic Lifetime Annotation describe the relationship between the lifetimes of multiple reference. They don't actually change the lifetime of a reference but rather just explain the relationship between different lifetimes.
- define a generic lifetime annotation after the function name
fn longest<'a>(x: &str, y: &str) -> &str {
- declare our generic lifetime annotation
fn longest<'a>(x: &'a str, y: &'a str) -> &str {
Something interesting to note here, the lifetime of our return value always has to be tied to the lifetime of one of our parameters. Why is that? It's because if we pass back a reference from a function, it has to be a reference to something that is passed in, and that's because we can't return a reference to something created inside the function.
fn longest2<'a>(x: &'a str, y: &'a str) -> &str {
let result = String::from("really long string");
result.as_str() // Error: cannot return reference to local variable `result`
}
struct ImportantExcerpt<'a> {
part: &'a str,
}
- In method signatures inside the
impl
block, references might be tied to the lifetime of references in the struct’s fields, or they might be independent. - In addition, the lifetime elision rules often make it so that lifetime annotations aren’t necessary in method signatures.
- normally, the lifetime of
self
is assigned to all output lifetime.
- normally, the lifetime of
impl<'a> ImportantExcerpt<'a> {
fn level(&self) -> i32 {
3
}
}
'static
, which means that this reference can live for the entire duration of the program.
All string literals have the 'static lifetime, which we can annotate as follows:
let s: &'static str = "I have a static lifetime.";