Rust Borrow Checker Explained Part 4: Structure Lifetimes
Published: 2023-11-11 21:13:47

## Intro

In part 3 we talked about how to tell the compiler what the lifetime of a function's result(s) are in relationship to the parameters of the function. In this article we will see how to add lifetimes to structures.

## Structural Lifetimes

Sometimes it is desirable to have structures which contain pointers, for example, our `Num` tuple that we talked about in **Part 1** could be borrowing the string instead of owning it:

```rust
struct Num(i32, &String);
```

...but the compiler will complain since we need to give the borrowed string a lifetime:

```
error[E0106]: missing lifetime specifier
 --> src/main.rs:1:17
```

To fix this, we declare lifetime(s) using template arguments, and then annotate the `&` borrows with the lifetime name. This is very similar to what we did for functions:

```rust
struct Num<'a>(i32, &'a String);
```

Let's try it out using code similar to what we did for **Part 1** when we implemented `Copy` marker trait:

```rust
#[derive(Debug, Copy, Clone)]
struct Num<'a>(i32, &'a String);

fn main() {
    let two_str = &String::from("two");
    let num = Num(2, two_str);
    let even = is_even(num);
    print!("is_even={even:?} num={num:?}")
}

fn is_even(num: Num) -> bool {
    num.0 % 2 == 0
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=9254c566bff3259fbae2d60be410ada7)

You will notice that since we are borrowing the `String`, we can actually implement the `Copy` trait!

With this lifetime information, the borrow checker can always ensure the lifetime of the borrowed str out lives the `Num` struct. Let's try it out:

```rust
#[derive(Debug, Copy, Clone)]
struct Num<'a>(i32, &'a String);

fn main() {
    let num;
    {
        let two_str = &String::from("two");
        num = Num(2, two_str);
    }
    let even = is_even(num);
    print!("is_even={even:?} num={num:?}")
}

fn is_even(num: Num) -> bool {
    num.0 % 2 == 0
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=84653d92fb8860737528442aa2fd3cba)

```
error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:7:24
```

The borrow checker observes that `two_str` lifetime is shorter than the `num` lifetime. Hurray for the borrow checker!

## Mutable Structural Borrows

Let's see if we can modify `is_even()` so that it has a side-effect of adding " is even" or " is odd" to the end of the `Num.1` String field:

```rust
#[derive(Debug)]
struct Num<'a>(i32, &'a mut String);

fn main() {
    let two_str = &mut String::from("two");
    let mut num = Num(2, two_str);
    let even = is_even(&mut num);
    print!("is_even={even:?} num={num:?}")
}

fn is_even(num: &mut Num) -> bool {
    if num.0 % 2 == 0 {
        *num.1 = format!("{} is even", num.1);
        return true;
    } else {
        *num.1 = format!("{} is odd", num.1);
        return false;
    }
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=486d6bb1677ace80756c053df7f3ef65)

Some important points:
* `mut` sprinkled all around. In particular:
  * `Num` declaration transitioned from `&'a` to `&'a mut`.
  * `Num` can **not** implement the `Copy` or `Clone` traits automatically, since it has a mutable borrow.
  * `two_str` is now a `&mut`.
  * Because `Num` does not implement `Copy`, we need to borrow it in the call to `is_even()`.
* We use the `*` to assign to the mutable reference.

If we modify the above so that `two_str` is printed at the end of main, we would get an error, since there can only be a single mutable reference... and the `Num` structure "took" that single mutable reference.

## Linked Lists

With this new found information, it might be tempting to use this new found information in order to define a single linked list of numbers like this:

```rust
struct Node<'a> {
    val: i32,
    next: Option<&'a mut Node<'a>>
}
```

Notice that we declare a lifetime, and we tell the borrow checker that the next node in the linked list is a borrowed node with this lifetime... recursively (since the next `Node` uses the same lifetime).

...and we can write this code that creates a linked list of 3 elements, then truncates it to 2:

```rust
#[derive(Debug)]
struct Node<'a> {
    val: i32,
    next: Option<&'a mut Node<'a>>
}

fn main() {
    let mut third = Node {
        val: 3,
        next: None,
    };
    let mut second = Node {
        val: 2,
        next: Some(&mut third),
    };
    let mut first = Node {
        val: 1,
        next: Some(&mut second),
    };
    println!("3 element LL={first:?}");
    (&mut first).next.as_mut().unwrap().next = None;
    println!("2 element LL={first:?}");
}
```
[Try it!](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=592813347d31d9460e4af2d41a7920bd)

However, this structure will be of limited practical use, since the lifetime of every element in the list must be the same or longer than the lifetime of the actual "owned" list elements. Additionally, the mutual exclusion property of mutable references means we can't even change the last println so it prints `second` since the mutable reference to it was transferred to `first`'s `next` field.

## Recap Of Structure Lifetimes

* We can use the `<'a>` syntax to declare a "template" lifetime to any structure.
* This declaration of lifetime(s) can be used in all the borrowed fields, and in other structures that require a lifetime parameter.
* Mutably borrowing inside of structures is impractical due to the mutual exclusion rules of mutable references. It also disables the ability to implement `Copy` and `Clone`.
* Immutable borrowing inside of structures is reasonable, but approach with caution, since you need these borrowed field lifetimes to be the same as (or longer than) the lifetime of an instance of that structure.

However, before you start `.clone()`ing everything into your structures, read [Part 5](https://dev.to/brimworks/rust-borrow-checker-explained-part-5-escape-hatches-4hk2) which discusses a few "escape hatches" that allow us to break all these borrow check rules.