How to simultaneously hold mutable borrows of two members of the struct wrapped by RefCell

Consider the following source:

use std::cell::RefCell;
use std::ops::DerefMut;

struct Point(usize, usize);

fn main() {
    let origin = RefCell::new(Point(400, 300));
    let mut br = origin.borrow_mut();

    let org = br.deref_mut();
    // let org = &mut br;
    
    let xmut = &mut org.0;
    let ymut = &mut org.1;
    
    *xmut = 800;
    *ymut = 600;
}

Here, origin is a Point struct wrapped into RefCell and is borrowed later as br with type RefMut. Then, borrow_mut from trait DerefMut is called and get org: &Point. Finally, each member can be mutably borrowed, simultaneously. (By the way, is this analysis correct?)


I noticed I can use dot syntax to access br: RefMut to get the corresponding member of the wrapped Point struct. For example, for accessing one single member:

    let org = &mut br;
    let xmut = &mut org.0;  // notice: only xmut and no ymut here
    *xmut = 800;

However, rustc compiles org is wrongly borrowed when the two member are mutably borrowed at the same time.

use std::cell::RefCell;
use std::ops::DerefMut;

struct Point(usize, usize);

fn main() {
    let origin = RefCell::new(Point(400, 300));
    let mut br = origin.borrow_mut();

    let org = &mut br; // here
    
    let xmut = &mut org.0; // now here are both xmut and ymut.
    let ymut = &mut org.1;
    
    *xmut = 800;
    *ymut = 600;
}
error[E0499]: cannot borrow `*org` as mutable more than once at a time
  --> src/main.rs:13:21

So two confusing questions have emerged:

First, why is dot syntax applicable to br: RefMut to access the members of Point struct? Is there an implicit trait method call?

Second, why does the &mut br crash but the explicit deref_mut() work? What's the difference between the two ways in sight of rustc? Besides, how to fix the program in &mut br way?

Thanks.

The first thing to note is that your deref_mut call is equivalent to this:

let org = &mut *br;

Notice the extra asterisk.

Anyway, the difference is that without the deref_mut call, your code is short-hand for this:

let xmut = org.deref_mut().0;
let ymut = org.deref_mut().1;

Borrowing each member simultaneously only works if there's not a function call in the way.

Where can I read further about the implicit org.deref_mut().1 when using dot syntax (Docs, Guides, Specifications, etc)?

See the documentation for DerefMut. There are links to additional documentation on this there.

Thanks, and one more question:

Should &mut * br be thought to be:

  • expanded to &mut * br.deref_mut()
  • or, a syntax sugar of br.deref_mut()

Well, as documented in the documentation for DerefMut, the expression *br is short-hand for *DerefMut::deref_mut(&mut br), so &mut *br is short-hand for this:

&mut *DerefMut::deref_mut(&mut br)

To understand the &mut * at the beginning here, the first thing to note is that it is operating on a mutable reference, so it doesn't expand to another deref_mut call. Instead, we need to see it as a place expression.

The expression *DerefMut::deref_mut(&mut br) is a place expression that represents the memory location that the mutable reference returned by deref_mut points at. So adding a &mut in front of that creates a new mutable reference pointing at that memory location.

Writing &mut *some_mutable_reference to create a new mutable reference to something you already have a mutable reference to is called a reborrow. In this particular case, the reborrow doesn't do anything, so you could remove it without changing anything. That is, &mut *br is also equivalent to this:

DerefMut::deref_mut(&mut br)

which is also the same as br.deref_mut().

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.