A lifetime is how long an object exists, i.e. the scope for which an object's reference is valid. Generally lifetimes are inferred in Rust, but sometimes annotations must be applied so the compile knows how long an object will be around for.
The syntax for explicit lifetimes are: 'a
, where the 'a' can be any char (well variable string, but who needs more than 26 lifetimes?).
The lifetime annotations live in the same area as templates, and share the same < >.
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let s;
let x = String::from("Apple");
{
let y = String::from("Pie");
s = longest(&x, &y);
println!("{}", s);
}
// print!("{}", s); // y does not live long enough, therefor s cannot be used here
}
Lifetimes have three shortcut rules to make it easier for developers:
fn foo<'a, 'b>(x: &'a str, y: &'b str)
fn foo<'a>(x: &'a str) -> &'a str
&self
or &mut self
, the lifetime of self
is automatically to all output lifetime parametersStatic lifetimes live for the entire duration of the program. They have the 'static
lifetime annotation.
fn main() {
let s;
{
let x: &'static str = "LONG LIVE THE STATIC!";
s = x;
}
println!("{}", s); // Static variable is still alive despite leaving scope
}
struct StringWrapper<'a> {
s: &'a str,
}
impl<'a> StringWrapper<'a> {
fn print(&self) {
println!("{}", self.s);
}
fn get_s(&self) -> &str { // &str does not need a lifetime annotation because of the 3rd shortcut rule
self.s
}
}
fn main() {
let wrapper;
{
let sentence = String::from("Apple Pie");
let first_word = sentence.split(' ').next().expect("Need more than 1 word");
wrapper = StringWrapper {
s: first_word,
};
wrapper.print();
};
// wrapper.print(); // first_word, who's lifetime is on sentence, does not live long enough
}