1

from the borrowing rule:

  • At any given time, you can have either one mutable reference or any number of immutable references.

Below, I have a mutable borrow of the struct Foo (the whole struct), which means I borrow every field of this struct. However, I can have another borrow to its field in demo function. And I suspect I have 2 mutable references to x.a:

#[derive(Debug)]
struct Foo {
    a: i32,
    b: i32,
}

fn demo(foo: &mut Foo) {
    // the `foo` is mutable borrow
    // should have exclusive access to all elements of Foo instance
    // However,
    let bar = &mut foo.a; // second ref to `x.a`
    *bar += 1;
    let baz = &foo.b;

    println!("{:?}", bar);
    println!("{:?}", baz);
}


fn main() {
    let mut x = Foo { a: 10, b: 1 };
    let foo = &mut x;  // ref to x, implies borrowing x.a and x.b
    demo(foo);
}

I know we can have disjoint mutable borrow to a struct binding (split borrow reference), but I'm not sure whether splitting the reference to a struct violates the borrow rule.

clarification: above code can compile. My confusion is that it should not compile due to the aforementioned reason. (playground)


I found this link relevant.

When you are borrowing a structure mutably, you are then free to borrow any of the sub-elements mutably.

But I can't find any doc to support it.

Izana
  • 2,537
  • 27
  • 33
  • Your request is not written in the form of asking "How do I fix this piece of code?" It is a little more philosophical. Does the above code work or not? "I suspect I have 2 mutable references" is not a coding question. Could you clarify what is not working? – Gardener Aug 16 '21 at 06:46
  • everything is working fine with rust. But I think above code violates the borrow rule and should not be compiled. – Izana Aug 16 '21 at 15:30

1 Answers1

5

The line

let bar = &mut foo.a;

creates a reborrow of the a field of foo. As long as the reborrow is alive, the field can't be accessed via foo.a anymore, but only via bar. After the last use of bar, you can use foo.a again – once the reborrow is released, the original borrow is available again.

Reborrowing is very similar to borrowing itself. While a value is borrowed, you cannot access the original value until the borrow is released. The same happens when reborrowing. This makes sure that at any given time there is only a single active mutable borrow, and the borrowing rules are upheld.

The fact that you are dealing with a struct here is incidental. You can do the same with a mutable borrow to any value, e.g. after

let mut i = 17;
let borrow = &mut i;
let reborrow = &mut *borrow;
*reborrow += 4;
*borrow += 21;

the value of i is 42. This code looks like there are multiple active mutable borrows to i. However, at any given time only one of them can be used. The reborrow is released after it is last used, so the original borrow becomes usable again. If we swap the last two lines in this code, it won't compile anymore, since the reborrow would still be alive when borrow is accessed, which is not allowed.

Unfortunately, the mechanics of reborrowing aren't currently documented in the Rust reference. A related, also not well documented topic are implicit reborrows that happen when a mutable reference is bound to a variable of a type that is already known to be a mutable reference.

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • 1
    In addition to your answer, it might be worth noting that attempting to reborrow the whole struct while the mutable borrow of `foo.a` is active will also trigger an error. E.g. replacing `baz` with `let baz = &foo;` ([playground](https://play.rust-lang.org/?version=stable&mode=release&edition=2018&gist=6fe100bb219463ff37f3d0051332f8b5)) – Jmb Aug 16 '21 at 09:53
  • that's different, isn't it? `&foo` wants to borrow a mutable borrow immutably. Without reborrow, it won't work either. – Izana Aug 16 '21 at 15:43
  • Thanks! It's pretty advanced. The borrow rule seems to be oversimplified. Could you guide me on which topic I should dig into to understand the effective scope of a reference? e.g., "The reborrow is released after it is last used, so the original borrow becomes usable again." – Izana Aug 16 '21 at 16:01
  • after some search, I found `Non-lexical lifetime` for reference is needed to understand the example from the above answer. https://stackoverflow.com/a/62959873/5335565 – Izana Aug 16 '21 at 16:29
  • 1
    @Izana An excellent explanation of non-lexical lifetimes is in [this blog post by Niko](http://smallcultfollowing.com/babysteps/blog/2017/02/21/non-lexical-lifetimes-using-liveness-and-location/). – Sven Marnach Aug 16 '21 at 18:10