Need help understanding lifetimes

Why below code compiles :-

fn strtok<'a>(s: &'a str, delimiter: char) -> &'a str {
    &s[0..5]
}

fn main() {
    let mut x = "hello world";
    let hello = strtok(&x, ' ');
    //println!("{}", hello);
    let mut z = &mut x;
    println!("{}", hello);
}

In the snippet borrow of x has same lifetime as hello which will last till last usage. And we are trying to borrow x mutably in between which shouldn't compile.

Your program compiles because:

  1. "hello world" is of type &'static str.
  2. Therefore, x is of type &'static str.
  3. Therefore, when it is passed to strtok, strtok can have 'a = 'static.
  4. Therefore, the return value, hello, can also be of type &'static str.
  5. Therefore, the &mut x borrow, of the variable x and not the string, of type &mut &'static str, does not conflict with other uses of the str.

(The &x is unnecessary in this program, because it’s making an &&'static str that is immediately auto-dereferenced, and can be replaced with just x.)

But if you don’t have &'static str, such as by changing the first line of main to this:

    let mut x = String::from("hello world");

then you will get the borrow conflict you expect.

1 Like

fn strtok<'a>(s: &'a str, delimiter: char) -> &'a str

this is immutably borrowing 'x' and returning same with 'a doesn't immutable borrow happens to be for 'a lifetime which will last till println!("{}", hello).

&str is always immutable no matter what, only it has a different slice of "hello world", in fact the real data is in .data section. Another thing to point out is that &str has the trait copy, thats why you really don't move x to hello

You can think of these two being the same:

let hello = strtok(&x, ' ');
let hello = strtok(&**&x, ' ');

Due to deref coercion. The latter pattern is also called reborrowing and happens implicitly in many places.

Reborrows don't borrow the intermediate references themselves, and their outer lifetime is limited only by the inner-most shared reference when there is one. In that case, in effect it acts like copying the shared reference. So effectively you passed in a copy of x.

(If x is not 'static, it still works. It works with x: &String too.)

1 Like