13

I understand you're not allowed to create two mutable references to an object at once in Rust. I don't entirely understand why the following code works:

fn main() {
    let mut string = String::from("test");
    let mutable_reference: &mut String = &mut string;
    mutable_reference.push_str(" test");
    // as I understand it, this creates a new mutable reference (2nd?)
    test(&mut *mutable_reference);

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

fn test(s: &mut String) {
    s.push_str(" test");
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Programmer9000
  • 1,959
  • 3
  • 17
  • 27

3 Answers3

14

The rule

There shall only be one usable mutable reference to a particular value at any point in time.

This is NOT a spatial exclusion (there CAN be multiple references to the same piece) but a temporal exclusion.

The mechanism

In order to enforce this, &mut T is NOT Copy; therefore calling:

test(mutable_reference);

should move the reference into test.

Actually doing this would make it unusable later on and not be very ergonomic, so the Rust compiler inserts an automatic reborrowing, much like you did yourself:

test(&mut *mutable_reference);

You can force the move if you wanted to:

test({ let x = mutable_reference; x });

The effect

Re-borrowing is, in essence, just borrowing:

  • mutable_reference is borrowed for as long as the unnamed temporary mutable reference exists (or anything that borrows from it),
  • the unnamed temporary mutable reference is moved into test,
  • at the of expression, the unnamed temporary mutable reference is destroyed, and therefore the borrow of mutable_reference ends.
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 1
    I am still having difficulty understanding this reborrow. If we speak in terms of lifetimes , `mutable_reference` life time begins when it is defined and goes out of scope only at the end of main scope. But in between the reborrow creates an anonymous mutable borrow whose lifetime is the scope of the `test` function. So isnt there are an intersection between the two lifetimes where two mutable reference to string exist? – raj Sep 25 '21 at 20:18
  • 1
    @raj: Yes. You really have insightful questions! Teaching is lying. You probably learnt about Newton's Physics in school? Much easier to teach that the Theory of Relativity, and "sufficient" to start, thus it's taught, even though it's known to be a "lie". The same applies here: when we say that a single mutable reference to any value may exist at any time, it's easy to memorize and remember, but it's not _quite_ true. The truth is that a single mutable reference to any value may be _accessible_ at any time, with the borrow-checker refusing access to re-borrowed references. – Matthieu M. Sep 26 '21 at 09:57
  • Thanks Matthieu. Coincidentally I just had a chat with an anti-vaxer with whom I had to use the Newton example to say "Proven wrong doesnt necessarily mean useless. Anyway Its an interesting aspect to know about mutable references. If u dont mind here's something similar. https://stackoverflow.com/questions/69335531/why-is-the-compiler-error-complains-about-multiple-mutable-reference-not-danglin . Is there any good resource to better understand reborrow and lifetimes ? – raj Sep 26 '21 at 13:48
  • @raj: I don't know of any resource other than the Book and Stack Overflow Q&As, unfortunately. – Matthieu M. Sep 27 '21 at 07:19
7

Is there more than one mutable pointer somewhere in memory referring to the same location? Yes.

Is there more than one mutable pointer in the code to the same location which is usable? No.

Re-borrowing a mutable pointer locks out the one you're re-borrowing from.

DK.
  • 55,277
  • 5
  • 189
  • 162
2

I analyzed the differences in MIR between test(mutable_reference) and test(&mut *mutable_reference). It appears that the latter only introduces an additional level of indirection:

code comparison: left is test(mutable_reference), right is test(&mut *mutable_reference)

When you are using an extra dereference in the function call, it doesn't create a mutable reference that holds; in other words, it doesn't cause any actual difference outside the function call.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
ljedrz
  • 20,316
  • 4
  • 69
  • 97