Validating References with Lifetimes

In this chapter, we'll explore ensure references are valid for as long as needed.

Validating References with Lifetimes

Lifetimes are another kind of generic. Rather than ensuring a type has the behavior we want, lifetimes ensure references are valid as long as we need them.

Every reference has a lifetime—the scope for which that reference is valid. Most of the time, lifetimes are implicit and inferred. We annotate lifetimes when the lifetimes of references could be related in a few different ways.

Preventing Dangling References

The main aim of lifetimes is to prevent dangling references:

module main
 
fn main() -> void {
    let r
    {
        let x = 5
        r = &x  // Error: `x` doesn't live long enough
    }
    println("r: ", r)
}

Lifetime Annotation Syntax

Lifetime annotations don't change how long references live. They describe relationships between lifetimes:

&i32        // A reference
&'a i32     // A reference with an explicit lifetime
&'a mut i32 // A mutable reference with an explicit lifetime

Lifetime Annotations in Functions

generic<'a>
fn longest(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        return x
    } else {
        return y
    }
}

This says: the returned reference will be valid as long as both input references are valid.

Lifetime Elision

The compiler can infer lifetimes in common patterns. These work without explicit lifetimes:

fn first_word(s: &str) -> &str { }

The compiler applies elision rules to figure out lifetimes.

Lifetimes in Structs

Structs can hold references with lifetime annotations:

module main
 
generic<'a>
struct ImportantExcerpt {
    part: &'a str,
}
 
fn main() -> void {
    let novel = String::from("Call me Ishmael. Some years ago...")
    let first_sentence = novel.split('.').next().unwrap()
    let excerpt = ImportantExcerpt {
        part: first_sentence,
    }
}

The Static Lifetime

The 'static lifetime means the reference can live for the entire program:

let s: &'static str = "I have a static lifetime."

All string literals have 'static lifetime.

Summary

Generics, traits, and lifetimes work together:

generic<'a, T> where T: Display
fn longest_with_announcement(x: &'a str, y: &'a str, ann: T) -> &'a str {
    println("Announcement! ", ann)
    if x.len() > y.len() { x } else { y }
}

Next, learn about Writing Automated Tests in Chapter 11.