Puzzle 21: Explanation
Let’s learn how Rust handles self-referential and borrowing.
We'll cover the following...
Test it out
Hit “Run” to see the code’s output.
#[derive(Debug)]struct Parser<'a> {body: String,subtext : &'a str,}fn main() {let mut document = Parser {body: "Hello".to_string(),subtext: ""};document.subtext = &document.body;let b = document;println!("{:?}", b);}
Output
The program fails to compile and produces the following message:
error[E0505]: cannot move out of `document` because it is borrowed
--> main.rs:14:13
Explanation
It’s not surprising that this example fails to compile. Setting up references within a structure to other parts of the structure looks like a code smell. What is surprising is that the compiler makes it nearly to the end of the code before it flags an error on the second-to-last line.
Structural references
Storing a reference in a struct is entirely valid, but we must provide a lifetime annotation for both the struct
and the reference. In this example, the structure itself has a lifetime specified: struct Parser<'a>
. The structure’s lifetime is tied to the stored reference: subtext : &'a str
. The lifetime syntax is illustrated as follows:
Connecting the struct lifetime to the reference’s lifetime helps Rust provide a lifetime guarantee. We can’t instantiate a variable of type Parser
unless the reference it contains is certain to remain valid longer than the structure’s lifetime.
Lifetime annotations allow Rust’s lifetime checker to help us. We ...