Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why rust ignore lifetime checks on &str?

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?

like image 948
Basemm Avatar asked Nov 27 '20 15:11

Basemm


People also ask

Why do we need lifetimes in Rust?

Lifetimes are what the Rust compiler uses to keep track of how long references are valid for. Checking references is one of the borrow checker's main responsibilities. Lifetimes help the borrow checker ensure that you never have invalid references.

How do Rust lifetimes work?

A lifetime is a construct the compiler (or more specifically, its borrow checker) uses to ensure all borrows are valid. Specifically, a variable's lifetime begins when it is created and ends when it is destroyed. While lifetimes and scopes are often referred to together, they are not the same.

What are lifetime parameters Rust?

Rust uses lifetime parameters to avoid such potential run-time errors. Since the compiler doesn't know in advance whether the if or the else block will execute, the code above won't compile and an error message will be printed that says, “a lifetime parameter is expected in compare 's signature.”

What does static lifetime mean Rust?

Static items have the static lifetime, which outlives all other lifetimes in a Rust program. Static items may be placed in read-only memory if the type is not interior mutable. Static items do not call drop at the end of the program.


3 Answers

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

like image 56
Mihir Luthra Avatar answered Oct 19 '22 20:10

Mihir Luthra


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.

like image 35
pretzelhammer Avatar answered Oct 19 '22 21:10

pretzelhammer


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.

like image 1
user1937198 Avatar answered Oct 19 '22 20:10

user1937198