About lifetime, why this code pass?

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
fn main() {
    let string2 = String::from("xyzfffssssssssssss");
    let string1 = String::from("long string is long");
    { //inner scope 1
        let result;
            { // inner scope 2 
                let str1_ref1= &string1[0..5];
                let str2_ref1= &string2[0..5];
                result = longest(str1_ref1,str2_ref1);
                
            } 
        println!("The longest string is {}", result);
    }   
}

Hi guys, I'm new to Rust, when reading the offical book (Validating References with Lifetimes - The Rust Programming Language). I typed above code which I think it should fail but actually pass. May I get some explanations?

My understanding is that both of lifetime of variable str1_ref1 and str2_ref1 are limited inside inner scope 2. When they are passed to function longest. The returned reference is exactly either str1_ref1 or str2_ref1 with lifetime must be in inner scope2 which is smaller than variable result's lifetime.

Does the rust compiler know that str1_ref1 is essentially referring to string1 which has larger lifetime? I read this chapter several times but got no clue on how this "fact" is revealed by the book.

What matters with lifetimes is not the scope of the reference (references are Copy), but of the owner. In this case, the owners of the data are string1 and string2 at the beginning of the function and they will only be dropped at the end of the function - they don't get used after that and thus this code is valid.

It would not compile if the strings were inside the inner block, like this: Rust Playground

2 Likes

Right… there is a distinction between the time a variable of type &T exists and the time the reference it contains is considered to be valid, the latter can be longer (or even shorter). This directly corresponds to the fact that when you have &T, there’s two things in (different places of) memory: the memory holding the reference address and the memory containing the T value itself, and commonly the T value itself exists for far longer than the address.

You could not return a &&str reference to &str1_ref1 from the inner block, as that would actually borrow the variable holding the &str’s address. But instead of borrowing str1_ref1 and str2_ref1 for the call to longest, you are merely copying their value, the result has no association to the original variables str1_ref1 and str2_ref1 anymore.

2 Likes