...

/

Puzzle 21: Explanation

Puzzle 21: Explanation

Let’s learn how Rust handles self-referential and borrowing.

Test it out

Hit “Run” to see the code’s output.

C++
#[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 ...