Search code examples
rustlifetimeborrow-checkerownershipmutable-reference

Rust Reborrowing Shared Reference from Mutable Reference


I'm new to Rust, and trying to understand why a mutable reference to a data structure that has had an element be borrowed can seemingly be used again within the same lifetime to borrow another element. This is namely in regards to an example from an O'Reilly book on Rust that has me somewhat confused:

let mut v = (136, 139);
let m = &mut v;
let m0 = &mut m.0;   //ok: reborrowing mutable from mutable
*m0 = 137;
let r1 = &m.1;       //ok: reborrowing shared from mutable,
                     //and doesn't overlap with m0
v.1;                 //error: access through other paths still forbidden
println!("{}", r1);  //r1 gets used here

The book mentions that:

Across the lifetime of a mutable reference, there is no other usable path to its referent or to any value reachable from there. The only references whose lifetimes may overlap with a mutable reference are those you borrow from the mutable reference itself.

The book then gives a diagram ("the ownership tree") showing a tree of values (whereby a child node is able to be referred to through a parent node, references permitting, such as having an element of a data structure where that element is the child node and parent node the data structure), with a mutable reference as giving that the path of references up from it must be inaccessible, and those for its referent and beneath it accessible only through it. Likewise for a shared reference, a similar tree diagram is given that shows all references on the path up from it, which can refer to it using that path, as having to be read-only, as well as that reference and those reachable from it being read-only.

So what namely confuses me is the 5th line of the above (let r1 = &m.1;), and why it's ok. I believe I understand that m simply borrows from v, which is fine; but then m0 borrows from m.0 (or is this technically from m?), which I would think per the above quote and tree diagram explanation means that m cannot be used here again until the end of m0's lifetime, because m refers to a data structure in memory on the conceptual path up from m0 in "the ownership tree." (Ie, m0's value is reachable from m.)

In sum, I would think that m can't be used again here after m0 borrows in the 3rd line above, but yet it is in the 5th line above to reference m.1. I'm trying to understand why that's ok. I have a feeling that it is because of the last line of the above quote in regards to "whose lifetimes may overlap", but it still feels somewhat contradictory. In the comments above it reads "and doesn't overlap with m0", even though m can reach both m.0 and m.1 and so appears above either in "the ownership tree", and so I would think be unusable/inaccessible (thereby preventing the access of m.1 thus through it in the 5th line) while a mutable reference to m.0 is alive.


Solution

  • As you've cited yourself

    The only references whose lifetimes may overlap with a mutable reference are those you borrow from the mutable reference itself.

    you're borrowing from the mutable reference, so that's fine. No where does it say you're restricted to borrowing mutably from it, and you're not as you've proven.

    In other words you can borrow however you like from a mutable reference, they even coerce to shared references.

    Because of non lexical lifetimes m0 doesn't have to go out of scope for m to be reborrowable even if it borrowed the same pieces as r1 did, but it doesn't even do that so the compiler is able to split the borrow as PitaJ pointed out.