Mutable Borrows (&mut)

The following code compiles:

fn main() {
    let mut s = String::from("hello world");
    let r1 = &mut s;

    //let r2 = &mut s;            // fails as expected
    let r2 = &mut *r1;
    println!("{r2}");

    println!("{r1}");
}

r2 succeeds in acquiring a second mutable borrow. I'd expect that this code doesn't compile. What am I misunderstanding here?

It's not a second borrow, it's reborrow of the first one. Note that, if you swap two printlns, the code will not compile:

error[E0502]: cannot borrow `r1` as immutable because it is also borrowed as mutable
 --> src/main.rs:6:16
  |
4 |     let r2 = &mut *r1;
  |              -------- mutable borrow occurs here
5 |     
6 |     println!("{r1}");
  |                ^^ immutable borrow occurs here
7 |     println!("{r2}");
  |                -- mutable borrow later used here
  |

That is, when you do r2 = &mut *r1, you temporarily render r1 unusable, as long as its reborrow is being used.

2 Likes

I understand if I uncomment the first let that I will get an error but not that the second let succeeds. Rust analyzers tells me that r2 is of type &mut String which appears to me that we now have two mutable borrows coexisting which shouldn't be possible from what I learned so far. After all the commented out let fails (as expected) because it tries to get another mutable borrow but the 2nd let succeeds in doing so.

You say this is a reborrow. I quickly searched the docs for the term reborrow but nothing showed up. Is there something I can read up on that explains this behavior?

Can it be that the NLL is in effect and disposes of r2? But then why does the NLL not dispose of r2 in the case below which does not compile:

    let r2 = &mut s; // fails as expected
    //let r2 = &mut *r1;
    println!("{r2}");

Point is, they're not coexisting - the first is temporarily non-existing while you're using the second, since the second is created from the first, not directly from the referenced object.

Bug which talks about how information about reborrowing should be added to the reference?

Yes, it's somewhat murky part of the language, but since it doesn't make valid code invalid it's low priority issue.

Thank you I will read through this later.

The Rust development philosophy, basically.

It's not really possible to write a set of rules which would both reject all unsound programs and accept all sound programs.

That's why set of rules in the Reference is conservative. All the programs which follow these rules are accepted, but not all programs which violate them are rejected because rust developers are regularly adjusting rules and try to accomodate more design patters. Sometimes without adding these exceptions to the documentation.

That's why you may suddenly find out that something which you expected to fail works, for some reason: unless you program is actually unsound (e.g. it creates two references which are actually alive at the same time) most likely cause is that you have hit some of these relaxations.

Of course if you can demonstrate that program is actually unsound… it's different story.

1 Like

There was a recentish thread with a lot of working through mental models for how reborrows work:

  • Starting here
    • Skip halfway down the comment to "Finally, I'd like to outline..."
    • The main reason to read this portion is it introduces a "chain of exclusive access" concept referred back to later
  • Then here through the end of the thread
    • In this part we examine why "no two &mut at the same time" is too simplistic of a model
    • And the following comments work through a few examples

It also contains links to some technical resources on the implementation and why they're sound (but I personally find those harder to consume).

3 Likes

I read the posted links here and I understand now roughly what a reborrow is, what purpose it serves and how it works (stacked borrow model).

That really needs to be mentioned in the book and the docs. Once you set out to test what you just learned about ownership and borrows you'll run into it and confusion sets in.

1 Like