Module 2 • Lesson 12

Lifetime Basics

📚 10 min read 💻 Free Course 🦀 nixus.pro

Lifetimes: Ensuring Valid References

Lifetimes prevent dangling references. The compiler tracks how long references live and rejects code where a reference might outlive the data it points to.

// ERROR: returning reference to local data
// fn dangle() -> &String {
//     let s = String::from("hi");
//     &s  // s dropped here - reference would dangle!
// }

// FIX: return owned String
fn no_dangle() -> String {
    String::from("hi") // Ownership moves to caller
}

// When compiler cannot infer, annotate with 'a:
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}
// 'a means: returned reference valid as long as shorter of x, y

fn main() {
    let s1 = String::from("long string");
    let result;
    {
        let s2 = String::from("xy");
        result = longest(s1.as_str(), s2.as_str());
        println!("{}", result); // OK: result used inside s2's scope
    }
    // println!("{}", result); // Would ERROR: s2 dropped
}

Lifetimes in Structs

// Struct holding a reference needs lifetime annotation
struct Excerpt<'a> {
    text: &'a str,
}

impl<'a> Excerpt<'a> {
    fn content(&self) -> &str {
        self.text  // Lifetime elided - rule 3 applies
    }

    fn announce(&self, msg: &str) -> &str {
        println!("Announcement: {}", msg);
        self.text
    }
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence;
    {
        let i = novel.find('.').unwrap_or(novel.len());
        first_sentence = &novel[..i];
    }
    let exc = Excerpt { text: first_sentence };
    println!("{}", exc.content());
}

// 'static: valid for entire program
// String literals are always 'static:
fn static_str() -> &'static str {
    "I live forever"
}

🎯 Practice

  1. Write fn longest_word<'a>(sentence: &'a str) -> &'a str returning the longest word
  2. Create struct StrPair<'a> { first: &'a str, second: &'a str } with a method returning the longer field
  3. Understand why this fails: fn bad<'a>() -> &'a str { let s = String::from("x"); &s }

🎉 Key Takeaways