Taking shared and mut references to parts of object hierarchy

Am trying to understand and validate the following statement in "Programming Rust" book

Shared access is read-only access. Values borrowed by shared references are read-only. Across the lifetime of a shared reference, neither its referent, nor anything reachable from that referent can be changed by anything. .......

From what I've understood from this, if you have an object hierarchy (say a->b->c), then if you take a shared reference to one of the objects in the middle (say b), then anything below that object (b and c) is read-only as long as the shared reference is live. Is my understanding correct ?

I tried the following contrived example to understand this:

#[derive(Debug)]
struct Middle {
    b:u32,
    left:Leaf,
    right:Leaf
}
#[derive(Debug)]
struct Leaf {
    a:u32
}

#[derive(Debug)]
struct Root {
    a:u32,
    b:Middle
}

fn array() {
    let mut v = [Root{a:20, b:Middle{b:30, left:Leaf{a:10}, right:Leaf{a:11}}},
                 Root{a:30, b:Middle{b:40, left:Leaf{a:20}, right:Leaf{a:21}}},
                 Root{a:40, b:Middle{b:50, left:Leaf{a:30}, right:Leaf{a:31}}},
                 Root{a:50, b:Middle{b:60, left:Leaf{a:40}, right:Leaf{a:41}}}];

    let r = &v[0].b.right;
    let m = &mut v[0].b.left;
    //let m = &mut v[2];   //compiler error
    println!("{}", r.a);
}

fn main() {
    array();
}

This code compiles. However if the commented out part of this code is un-commented, it fails with:

error[E0502]: cannot borrow `v[_]` as mutable because it is also borrowed as immutable
  --> src/main.rs:26:13
   |
24 |     let r = &v[0].b.right;
   |             ------------- immutable borrow occurs here
25 |     //let m = &mut v[0].b.left;
26 |     let m = &mut v[2];   //compiler error
   |             ^^^^^^^^^ mutable borrow occurs here
27 |     println!("{:}", r.a);
   |                     --- immutable borrow later used here

Does this mean not only the parts that can be reachable by the referent but the path leading up to the referent is also read-only ?

In order to get a shared reference to v[0].b.right, the compiler takes a shared reference to v, looks up index [0], takes a reference to b, and then finally to right. All of those shared references are (mostly) kept alive as long as the final reference to right is. It’s the reference to v that’s causing a problem here, because you need to get &mut v before you can assign into an index.

&mut v[0].b.left doesn’t cause a problem because the compiler has special reasoning to let you take a reference to one of a struct’s fields without preventing mutable access to the others. This is also possible for vectors and slices, but not automatically: you have to use methods like slice::split_first_mut to get concurrent access to different elements.

The compiler is not able to reason about array indexing, so the entire array is marked borrowed when any index is accessed. You can split the slice with one of the split methods or a mutable iterator to only borrow parts of the array.

@2e71828 Thank you for the explanation. Regarding &mut v[0].b.left - the special reasoning only applies to structs ? Where can I find such information (in this case the special reasoning by the compiler ) ?

@alice Got it. Thanks.

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.