In this lesson, you will learn how to enforce safety when your structs hold references by tethering their lifetime to the data they point to.
The Problem of Stored References
Up until now, you have mostly worked with structs that "own" their data. When a struct owns a String or a Vec, it manages that memory directly. But what if you want a struct to simply point to existing data?
If you try to store a reference in a struct without further instructions, the Rust compiler will stop you. It needs to ensure that the struct itself never outlives the data it points to. If the original data were dropped while the struct still held a reference to it, you would end up with a dangling pointer—a classic memory safety violation.
Establishing the Dependency
To fix this, we use lifetime annotations to create a Structural Lifetime Dependency. By adding a lifetime parameter to the struct definition, we tell the compiler: "This struct cannot live longer than the reference it holds."
Consider this example:
struct BookReview<'a> {
text: &'a str,
}
fn main() {
let comment = String::from("This book is fantastic!");
// The struct 'review' is now tethered to 'comment'
let review = BookReview {
text: &comment,
};
println!("{}", review.text);
}
In this code, 'a acts as a placeholder for the lifetime of the comment variable. By declaring BookReview<'a>, we explicitly state that the struct is dependent on a reference that lives at least as long as 'a. If you were to try and use review after comment went out of scope, the borrow checker would immediately flag it, preventing a crash or memory corruption.
The struct is no longer an independent entity; it is now structurally dependent on the context from which it borrowed its data. This ensures your data structures remain valid throughout their entire existence.
Key Takeaway: Adding a lifetime parameter to a struct creates a structural dependency that forces the compiler to guarantee the struct cannot outlive the data it references.