Why rust ignore lifetime checks on &str?

736 views Asked by At
fn main() {
    let strA = "a";
    let result;

    {
        let strB = "abc";
        result = longest(strA, strB); // Will return strB
    }

    println!("The longest string is {}", result); // result now point to strB!!
}

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

As I got from the Rust book

'a will get the concrete lifetime that is equal to the smaller of the lifetimes of x and y

So why strB is now visible outside of its scope?

3

There are 3 answers

0
Mihir Luthra On BEST ANSWER

That's because all string literals have 'static lifetime. From the rust book:

One special lifetime we need to discuss is '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.";

The text of this string is stored directly in the program’s binary, which is always available. Therefore, the lifetime of all string literals is 'static

0
user1937198 On

the lifetime 'a refers to the lifetime of the string buffer str not the reference to that buffer. So the lifetime of the &str strB is within the block. However, "abc" is a constant string. This means that the storage of the str buffer has 'static lifetime, meaning it is guaranteed to outlast any other lifetime. As such it is valid for result to reference that buffer even after strB no longer references it.

0
pretzelhammer On

Fixed example:

fn main() {
    let strA = "a".to_string();
    let result;

    {
        let strB = "abc".to_string();
        result = longest(&strA, &strB); // Will return strB
    }

    println!("The longest string is {}", result); // compile error
}

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

Now produces a compiler error as expected:

error[E0597]: `strB` does not live long enough
  --> src/main.rs:7:33
   |
7  |         result = longest(&strA, &strB); // Will return strB
   |                                 ^^^^^ borrowed value does not live long enough
8  |     }
   |     - `strB` dropped here while still borrowed
9  | 
10 |     println!("The longest string is {}", result); // result now point to strB!!
   |                                          ------ borrow later used here

playground

Rust was not "ignoring" the lifetimes of the string variables in your initial example. When you set a variable to a string literal that literal gets hardcoded into the executable binary and gets a 'static lifetime which means it's valid for the entire duration of the program. If we add explicit type annotations to your initial example it should become clear why it compiles and works:

fn main() {
    let strA: &'static str = "a";
    let result;

    {
        let strB: &'static str = "abc";
        result = longest(&strA, &strB); // returns 'static str
    }

    println!("The longest string is {}", result); // prints result
}

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

However when we call to_string() on a string literal we create an owned and heap allocated String which lifetime's is non-static and is scoped to whatever block its in, hence making the change makes the program no longer compile as expected.