1
votes

In Rust book (https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html), this code is used as example (paraphrased):

fn main() {
    let string1 = String::from("long string is long");
    {
        let string2 = String::from("xyz");
        let result = longest(string1.as_str(), string2.as_str()); // line 5 
        println!("The longest string is {}", result);  // line 6
    }
}

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

I am confused why this code compiles at all.

Regarding the longest function, the book says, "the generic lifetime 'a will get the concrete lifetime that is equal to the smaller of the lifetimes of x and y".

The book then talked as if string1.as_str() and string2.as_str() live as long as string1 and string2 respectively. But why would they? These two references were not used after line 5, and by line 6, they should have been dead. Why there wasn't an error at line 6 for using result when it is no longer live?

One could say that presence of result somehow extends the input lifetimes, but wouldn't that contradict the notion that "output lifetime is the intersection of input lifetimes"?

Where do I get it wrong?

2

2 Answers

1
votes

But why would they? These two references were not used after line 5, and by line 6, they should have been dead.

But they're not dead. In fact, one of them is definitely in result and is getting used on Line 6. A reference can last, at minimum, until the end of the current expression (generally, but not always, until a semicolon), and at maximum as long as the thing it points to continues existing. The lifetime parameter from the output of longest requires that it last as long as result is in scope. Notably, the scope of result is no larger than the scope of either string1 or string2, so there's no issue. If we tried to assign the result of longest to a variable that outlives string2, then we'd have a problem. For instance, this won't compile.

fn main() {
    let string1 = String::from("long string is long");
    let mut result = "";
    {
        let string2 = String::from("xyz");
        result = longest(string1.as_str(), string2.as_str());
    }
    println!("The longest string is {}", result);
}

Because that would require result to outlive string2, which is a problem.

0
votes

The confusion seems to me to originate in the type &'a str. The 'a is the lifetime of the data to which the reference refers, i.e. the region of validity of that data. It is not the region for which the reference variable itself is valid.

So...

string1.as_str() returns a &'s1 str, where 's1 is the lifetime of string1.

string2.as_str() returns a &'s2 str, where 's2 is the lifetime of string2.

longest must infer a single generic lifetime 'a from two parameter lifetimes 's1 and 's2. It does this by choosing the common overlap, the shorter lifetime 's2. This is a form of subtyping: references valid for longer lifetimes can be used transparently as references valid for shorter lifetimes.

So result is a &'s2 str.

Line 6 references result of type &'s2 str where 's2 is the lifetime of string2. The reference name itself is available, and the referent data is valid for the lifetime of string2, 's2, which we are still within.

string1.as_str() and string2.as_str() live as long as string1 and string2 respectively

That's loosely worded. They are references, and they do not last past line 5. But they are references with a lifetime equal to the lifetime of string1 and string2 respectively. This means the data they point to is to live at least that long.

Why there wasn't an error at line 6 for using result when it is no longer live?

result has type &'s2 str, so it is valid. It is a copy of one of the two (temporary) references which are inputs to longest in line 5. The name result is valid in line 6, and it points to data which is guaranteed to live as long as 's2 which is still valid. So, there is no error.

One could say that presence of result somehow extends the input lifetimes

There is no such extension. Those temporaries do not exist past line 5. But one of them is copied into result, and the lifetime is not how long the temporary lives (how long the input reference &'a str lives), but how long the referent data lives.