So, you've decided to deep dive into Rust lifetime parameters. Nice!
Let's talk about when dinosaurs roamed the earth.
SIMPLE CASE, COMPILER APPROVED ✅
this simple snippet in a Rust sandbox
#[derive(Debug)]
struct Earth {
location: String,
}
#[derive(Debug)]
struct Period {
age: String,
}
#[derive(Debug)]
struct Dinosaur {
location: Earth,
period: Period,
name: String,
}
fn main() {
let montana = Earth {
location: String::from("Montana"),
};
let jurassic = Period {
age: String::from("Jurassic"),
};
let t_rex = Dinosaur {
name: String::from("Tyrannosaurus Rex"),
location: montana,
period: jurassic,
};
println!("{:?}", t_rex.name);
println!("{:?}", t_rex.location);
println!("{:?}", t_rex.period);
// compiler disallows these next lines, why?
// println!("{:?}", montana);
// println!("{:?}", jurassic);
}
Our aim here is to create a Rust compiler-approved Dinosaur struct, incorporating Earth
and Period
structs. This is what we have after a first pass of putting some props into a few struct
objects.
At the bottom of the snippet is a commented-out line that the compiler does not allow. Why? Because montana
and jurassic
are now owned by t_rex
and access to their values is now only allowed via t_rex
. This is not how we want the code to behave.
Some more change is left to be done with the code, but this is the basic idea. We want a dinosaur to have knowledge of its location and its time period... but definitely not for the struct
itself to own the Earth
and Period
structs.
SLIGHTLY LESS NAÏVE APPROACH, NOT COMPILER-FRIENDLY YET ❌
Now let's say you decide to start experimenting with Rust references and come up with this idea:
struct Dinosaur {
location: &Earth,
period: &Period,
name: String,
}
But even before running any business logic between the structs, the compiler already wants to know more, it wants explicit lifetime parameters on Dinosaur
, so you dig around and put this together:
LIFETIME PARAMETER INFORMED CASE, COMPILER APPROVED ✅
this lifetime snippet in a Rust sandbox
#[derive(Debug)]
struct Earth {
location: String,
}
#[derive(Debug)]
struct Period {
age: String,
}
// instead of receiving the values `location` and `period`,
// Dinosaur borrows (pre-existing) instances of Earth, Period
#[derive(Debug)]
struct Dinosaur<'a> {
location: &'a Earth,
period: &'a Period,
name: String,
}
fn main() {
let montana = Earth {
location: String::from("Montana"),
};
let jurassic = Period {
age: String::from("Jurassic"),
};
let t_rex = Dinosaur {
name: String::from("Tyrannosaurus Rex"),
location: &montana,
period: &jurassic,
};
println!("{:?}", t_rex.name);
println!("ACCESSING MONTANA DIRECTLY {:?}", montana);
println!("ACCESSING JURASSIC DIRECTLY {:?}", jurassic);
}
In a nutshell:
-
Dinosaur
is receivinglocation
andperiod
as borrowed references. - Since
Dinosaur
does not own the values, the Rust compiler wants explicit confirmation that the original value that the references refer to will still be valid as long asDinosaur
needs them. - This explicit confirmation comes in the form of lifetime parameters, on
Dinosaur<&'a>
.
HANDLING SEVERAL LIFETIMES IN A SINGLE STRUCT
"But why doesn't each prop in Dinosaur
gets its own letter? What about this <'a, 'b>
that I saw somewhere on Stack Overflow? How does that work?"
Oh, you mean something like Dinosaur<'a, 'b>
, right? So each of the letters represents a different scope that the struct knows about. Here is a link to the official documentation, but the important part of it right now is the following snippet, illustrating what the lifetime parameters signify in terms of scopes.
{
let r; // ---------+-- 'a
// |
{ // |
let x = 5; // -+-- 'b |
r = &x; // | |
} // -+ |
// |
println!("r: {}", r); // |
} // ---------+
I'll leave integrating this stuff into our Dinosaur
example up to you, o dear reader of mine.
Top comments (3)
I think it's important to point out that T-rex actually lived in the Cretaceous period ;)
I knew someone would find me out ;P
I was literally reading "The Rise and Fall of the Dinosaurs: A New History of a Lost World" when reading the post, otherwise probably wouldn't have noticed :)