Lifetime - the span of the program which a specific place of data is valid.
The Borrow Checker evaluates the lifetimes of values in order to make sure the Borrowing rules are not being broken.
the goal is to check if:
The references lifetime does not outlive the owner value's lifetime (the original)
Last Example:
fn main() {
let s1 = String:from("hello world");
let s2 = String:from("hello world again");
let result: &str = longest(s1.as_str(), s2.as_str());
println!("The longest string is {}", result);
}
// ERROR: missing lifetime specifier
fn longest(a: &str, b: &str) -> &str {
if a.len() > b.len() {
a
} else {
b
}
}
Concrete vs Generic Lifetimes
concrete == the span of code where the ownership is valid and that the compiler infers.
but generic lifetimes are a type of generic that inform the compiler how references to data should be related to each other and ensuring that data lives long enough to be accessed.
take these for example
Concrete Lifetimes
fn get_static_str() -> &'static str {
"I live forever!"
// This string lives for the entire duration of the program
}
fm main() {
let s: &'static str = get_static_str();
println!("{}", s);
}
Generic lifetime
fn get_str_with_lifetime<'a>(input: &'a str) -> &'a str {
input
// the returned reference has the same lifetime as the input
}
fn main() {
let input = String::from("hello");
let result = get_str_with_lifetime(&input);
println!("{}", result);
// safe because input and result have the same lifetime.
}
Here, 'a
is a generic lifetime. It doesn't specify how long the reference will live; it only guarantees that result
will not outlive input
.
In short:
- Concrete lifetimes are explicit (like
'static
). - Generic lifetimes allow for flexibility in how long references can live while ensuring safety.