2

First, I know there are many similar questions, and I've read many discussions, but I still cannot understand what's going on in my case.

struct Test {
    x: u32,
}

fn main() {
    let mut t = & mut Test { x: 0 };
    println!("{}", t.x);
    t = & mut Test { x: 1 };
    println!("{}", t.x);
}

Ok, this gives an error as expected:

error[E0716]: temporary value dropped while borrowed
 --> src/main.rs:8:15
  |
8 |     t = & mut Test { x: 1 };
  |               ^^^^^^^^^^^^^- temporary value is freed at the end of this statement
  |               |
  |               creates a temporary value which is freed while still in use

"creates a temporary value which is freed while still in use" – that is right, but why doesn't this error happen before? It happens only on on the second assignment of &mut, whereas the first one is fine. Shouldn't it also create a temporary value that is dropped? This is what I can't understand.

Moreover, if I remove mut then everything runs fine:

fn main() {
    let mut t = & Test { x: 0 };
    println!("{}", t.x);
    t = & Test { x: 1 };
    println!("{}", t.x);
}

But why? A non-mutable reference is still a reference, it still points to some data, and the temporary values are still dropped. So references should be invalid. Should also give error, but no, it doesn't. Why?

But wait, there's more! If I implement Drop trait for Test:

impl Drop for Test {
    fn drop(& mut self) {
        
    }
}

And now I get the same error again, without changing the code in main(). How is it related?

Andrei Nikolaenko
  • 1,022
  • 1
  • 7
  • 13
  • @cafce25 it's somewhere close. It can explain why it is legal to borrow a temporary. (Answer: via the hidden intermediate ```let tmp = ...```). But it does not answer the question why is it working first time and not working the second time (however it will work with the explicit ```let tmp = ...```). Also does not answer the question why immutable refs work fine any time. So I don't know, I'll need more reading into it. – Andrei Nikolaenko Apr 15 '23 at 13:37

1 Answers1

3

Why

let mut t = &mut Test { x: 0 };

works is described in Why is it legal to borrow a temporary? and the reference:

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.

t = &mut Test { x: 1 };

doesn't work because it's not a let statement and therefore not eligible for the same lifetime promotion.

let mut t = &Test { x: 0 };

allows the temporary to be promoted to a static (because it's immutable) and the following is eligible for constant promotion, too:

t = &Test { x: 1 };

so it works.

Since a Drop implementation changes semantics of a static vs a local variable anything that implements it isn't eligible for constant promotion so the problem reappears (references to things that implement Drop are only eligible for the same scope extension as mutable references). The linked reference article contains a more comprehensive explanation of all of this.

cafce25
  • 15,907
  • 4
  • 25
  • 31
  • While I still don't understand it fully, I see that your answer gives the correct information, I just need time to grasp it. Therefore I will mark your answer as the answer and I maybe will come back with further clarifications as to make my question and your answer more useful. – Andrei Nikolaenko Apr 15 '23 at 14:10