Lifetimes in Data Structures

If a data type stores borrowed data, it must be annotated with a lifetime:

1. ```
   #[derive(Debug)]
   ```
2. ```
   struct Highlight<'doc>(&'doc str);
   ```

4. ```
   fn erase(text: String) {
   ```
5. ```
       println!("Bye {text}!");
   ```
6. ```
   }
   ```

8. ```
   fn main() {
   ```
9. ```
       let text = String::from("The quick brown fox jumps over the lazy dog.");
   ```
10. ```
        let fox = Highlight(&text[4..19]);
    ```
11. ```
        let dog = Highlight(&text[35..43]);
    ```
12. ```
        // erase(text);
    ```
13. ```
        println!("{fox:?}");
    ```
14. ```
        println!("{dog:?}");
    ```
15. ```
    }
    ```

This slide should take about 5 minutes.

  • In the above example, the annotation on Highlight enforces that the data underlying the contained &str lives at least as long as any instance of Highlight that uses that data.
  • If text is consumed before the end of the lifetime of fox (or dog), the borrow checker throws an error.
  • Types with borrowed data force users to hold on to the original data. This can be useful for creating lightweight views, but it generally makes them somewhat harder to use.
  • When possible, make data structures own their data directly.
  • Some structs with multiple references inside can have more than one lifetime annotation. This can be necessary if there is a need to describe lifetime relationships between the references themselves, in addition to the lifetime of the struct itself. Those are very advanced use cases.