Reputation: 328
This code triggers a compiler error:
fn main() {
let s = "FOO";
let s_lower = s.to_lowercase().as_str();
println!("{}", s_lower);
}
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:3:19
|
3 | let s_lower = s.to_lowercase().as_str();
| ^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary value which is freed while still in use
4 |
5 | println!("{}", s_lower);
| ------- borrow later used here
However, this code does not:
fn main() {
let s = "FOO";
let s_lower = &s.to_lowercase();
println!("{}", s_lower);
}
Why? Is it because &temp_string
is a &String
which is different from &str
? If so, how?
Upvotes: 0
Views: 728
Reputation: 328
Several links from @kmdreko & @sudo are helpful.
I understand why s.to_lowercase().to_str()
does not compile but wondered why &s.to_lowercase()
does not have the same problem.
This is called Temporary lifetime extension, the doc says:
The temporary scopes for expressions in let statements are sometimes extended to the scope of the block containing the let statement. This is done when the usual temporary scope would be too small, based on certain syntactic rules. For example:
let x = &mut 0;
// Usually a temporary would be dropped by now, but the temporary for `0` lives
// to the end of the block.
println!("{}", x);
I find this special rule a bit arbitrary. Here's a related discussion on why it does not apply to temp.as_bytes()
which I resonate.
Surprisingly there's a RFC which seemed to want to make this more regular, but somehow is not applicable here.
This article explains why Temporary Lifetime Extension is desired in some cases.
I found this older rust references helpful with understanding temporary values as well, with a lot of examples.
Upvotes: 1
Reputation: 548
There are a two points to make here.
&String
and &str
are different types. The former is a reference to a dynamic heap string type, the latter is a string slice which is a reference to part of a string. I recommend reviewing this chapter of the Rust book on slices.In the first example, the return type of to_lowercase()
is String
- it is returning a String
that will be owned by the caller. However we are not binding this return value to a variable - we're attempting to call as_str()
first and then binding the result of that to s_lower
. as_str()
can't be chained with to_lowercase()
without first binding the result because it needs to take a reference to the calling String
, which can't be done on a temporary. This is problematic because the String
returned by to_lowercase()
ends up being dropped (deallocated and unusable) before as_str()
is called as the compiler realizes it can't be used.
To make it clearer, he first example is sort of equivalent to:
fn main() {
let s = "FOO";
// temporary scope
{
let temporary = s.to_lowercase();
}
let s_lower = temporary.as_str();
}
based on the temporary value rules The lifetime of the temporary is hopefully clearer this way. To fix the first example you would have to do:
fn main() {
let s = "FOO";
let s_lower = &s.to_lowercase();
let s_lower_slice = s_lower.as_str();
println!("{s_lower_slice}");
}
which is further explained in the answer @kmdreko linked above
Upvotes: 0