0
fn main() {
    let s = "FOO";
    let s_lower = s.to_lowercase().as_str();

    println!("{}", s_lower);    
}

says

3 |     let s_lower = s.to_lowercase().as_str();
  |                   ^^^^^^^^^^^^^^^^         - temporary value is freed at the end of this statement

However,

fn main() {
    let s = "FOO";
    let s_lower = &s.to_lowercase();

    println!("{}", s_lower);    
}

is fine.

Why? Is it because &temp_string is a &String which is different from &str? If so, how?

Thanks!

zl9394
  • 318
  • 3
  • 5
  • 1
    The reason is that `let x = &…;` is ["special"](https://doc.rust-lang.org/nightly/error-index.html#E0716): "Temporaries are not always dropped at the end of the enclosing statement. […]" – Caesar Feb 23 '22 at 01:06
  • 1
    Does this answer your question? [Why is it legal to borrow a temporary?](https://stackoverflow.com/q/47662253) – kmdreko Feb 23 '22 at 04:27

2 Answers2

1

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.

zl9394
  • 318
  • 3
  • 5
0

There are a two points to make here.

  1. Yes, &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.
  2. The lifetime of the temporary is the real culprit as the error message indicates.

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

sudo
  • 503
  • 4
  • 12