Skip to content

Latest commit

 

History

History
122 lines (81 loc) · 4.12 KB

rust_lifetime.md

File metadata and controls

122 lines (81 loc) · 4.12 KB

Rust Lifetimes

Every reference in Rust has a lifetime, which is the scope for which that reference is valid.

Most of the time, lifetimes are implicit and inferred. The borrow checker was able to figure all out for us without any help.

But sometimes we must annotate lifetimes when the lifetimes of references could be related in a few different ways.

// [E0106] missing lifetime specifier 
// this function's return type contains a borrowed value, 
// but the signature does not say whether it is borrowed from `x` or `y` 
fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn main() {
    let string1 = String::from("abcd");
    let string2 = "xyz";

    let result = longest(string1.as_str(), string2);
    println!("The longest string is {}", result);
}
  • As the borrow checker, how would you know that result is not a dangling reference ? What's the lifetime of result.
  • THe longest function return a reference -> &str, but we have no idea what the lifetime of this reference is.
    • first of all, we return either x or y, x could have a different lifttime than y.
    • second of all, we don't know the exact lifetime of x or y because these are just placeholders.
      • image that this function gets called from many different places, so x and y could have many different lifetimes.
  • the borrow checker doesn't know how to handle this ambiguity, and so we get an error.

To solve this error, we need something called generic lifetime annotation.

Generic Lifetime Annotation in Function Signatures

Generic Lifetime Annotation describe the relationship between the lifetimes of multiple reference. They don't actually change the lifetime of a reference but rather just explain the relationship between different lifetimes.

  1. define a generic lifetime annotation after the function name
    fn longest<'a>(x: &str, y: &str) -> &str {
  2. declare our generic lifetime annotation
    fn longest<'a>(x: &'a str, y: &'a str) -> &str {

Something interesting to note here, the lifetime of our return value always has to be tied to the lifetime of one of our parameters. Why is that? It's because if we pass back a reference from a function, it has to be a reference to something that is passed in, and that's because we can't return a reference to something created inside the function.

fn longest2<'a>(x: &'a str, y: &'a str) -> &str {
    let result = String::from("really long string");
    result.as_str() // Error: cannot return reference to local variable `result`
}

Generic Lifetime Annotations in Struct Definitions

struct ImportantExcerpt<'a> {
    part: &'a str,
}

Generic Lifetime Annotations in Method Definitions

  • In method signatures inside the impl block, references might be tied to the lifetime of references in the struct’s fields, or they might be independent.
  • In addition, the lifetime elision rules often make it so that lifetime annotations aren’t necessary in method signatures.
    • normally, the lifetime of self is assigned to all output lifetime.
impl<'a> ImportantExcerpt<'a> {
    fn level(&self) -> i32 {
        3
    }
}

The Static Lifetime

'static, which means that this reference can live for the entire duration of the program.

All string literals have the 'static lifetime, which we can annotate as follows:

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