1

Can someone explain which exact temporary value is dropped and what the recommended way to do this operation is?

fn main() {
    let mut a = &mut String::from("Hello Ownership");
    a = &mut a.replace("Ownership", "World");
    println!("a is {}", a);
}
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Noah
  • 57
  • 1
  • 4
  • 1
    I asked a concise Question for common rust error `error[E0716]` [error E0716: temporary value dropped while borrowed (rust)](https://stackoverflow.com/questions/71626083/error-e0716-temporary-value-dropped-while-borrowed-rust). It links back to this Question. – JamesThomasMoon Mar 26 '22 at 07:26

3 Answers3

4

If you want to keep the &mut references (which are generally not needed in your case, of course), you can do something like this:

fn main() {
    let a = &mut String::from("Hello Ownership");
    let a = &mut a.replace("Ownership", "World");
    println!("a is {}", a);
}

The type of a would by &mut String. In the second line we do what's known as variable shadowing (not that it's needed) and the type is still &mut String.

That doesn't quite answer your question. I don't know why exactly your version doesn't compile, but at least I thought this info might be useful. (see below)

Update

Thanks to Solomon's findings, I wanted to add that apparently in this case:

let a = &mut ...;
let b = &mut ...;

or this one (variable shadowing, basically the same as the above):

let a = &mut ...;
let a = &mut ...;

, the compiler will automatically extend the lifetime of each temporary until the end of the enclosing block. However, in the case of:

let mut a = &mut ...;
a = &mut ...;

, it seems the compiler simply doesn't do such lifetime extension, so that's why the OP's code doesn't compile, even though the code seems to be doing pretty much the same thing.

at54321
  • 8,726
  • 26
  • 46
1

Why are you using &mut there? Try this:

fn main() {
    let mut a = String::from("Hello Ownership");
    a = a.replace("Ownership", "World");
    println!("a is {}", a);
}
Solomon Ucko
  • 5,724
  • 3
  • 24
  • 45
  • Yeah, I know this would work. But I am just curious about which part of line 3 is considered the temporary value and is dropped. Could you elaborate a little more? Thank you. – Noah Feb 12 '22 at 07:46
  • @Noah `a.replace("Ownership", "World")` returns an owned `String` that would be treated as temporary and `drop`ped at the end of the statement. – Solomon Ucko Feb 12 '22 at 13:59
  • 1
    @SolomonUcko Question is (for me at least) why is that the case, i.e. why does the example in my answer work and the one in OP's question not. – at54321 Feb 13 '22 at 06:54
  • @at54321 Hmm, good point. I'm not completely sure but I would guess that it has to do with determining the lifetime of the reference. – Solomon Ucko Feb 13 '22 at 13:51
  • @at54321 Figured it out, see my new answer: https://stackoverflow.com/a/71104068/5445670 – Solomon Ucko Feb 13 '22 at 19:20
1

Aha, figured it out!

https://doc.rust-lang.org/nightly/error-index.html#E0716 says:

Temporaries are not always dropped at the end of the enclosing statement. In simple cases where the & expression is immediately stored into a variable, the compiler will automatically extend the lifetime of the temporary until the end of the enclosing block. Therefore, an alternative way to fix the original program is to write let tmp = &foo() and not let tmp = foo():

fn foo() -> i32 { 22 }
fn bar(x: &i32) -> &i32 { x }
let value = &foo();
let p = bar(value);
let q = *p;

Here, we are still borrowing foo(), but as the borrow is assigned directly into a variable, the temporary will not be dropped until the end of the enclosing block. Similar rules apply when temporaries are stored into aggregate structures like a tuple or struct:

// Here, two temporaries are created, but
// as they are stored directly into `value`,
// they are not dropped until the end of the
// enclosing block.
fn foo() -> i32 { 22 }
let value = (&foo(), &foo());
Solomon Ucko
  • 5,724
  • 3
  • 24
  • 45
  • 1
    Thanks, Solomon, good finding! Now it makes sense - the compiler simply doesn't do the same trick in the OP's case. I updated my answer as well. – at54321 Feb 13 '22 at 19:47