Reputation: 263
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?
Upvotes: 1
Views: 208
Reputation: 7383
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()
andstring2.as_str()
live as long asstring1
andstring2
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.
Upvotes: 0
Reputation: 70257
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.
Upvotes: 1