Do references to mutable references need to be mutable?

Usually, an immutable &mut can be used to mutate the data it refers to:

fn main() {
    let mut v = vec![];
    let r = &mut v; // r itself is immutable
    
    r.push(0); // an immutable &mut is used to mutate v
    
    println!("{:?}", v);
}

(playground)

However, when I attempt to take a & to the &mut, the code no longer compiles:

fn main() {
    let mut v = vec![];
    let r = &mut v;
    
    let rr = &r; // &&mut
    rr.push(0); // use a &&mut to mutate the data
    
    println!("{:?}", v);
}
error[E0596]: cannot borrow `**rr` as mutable, as it is behind a `&` reference
 --> src/main.rs:6:5
  |
5 |     let rr = &r;
  |              -- help: consider changing this to be a mutable reference: `&mut r`
6 |     rr.push(0);
  |     ^^ `rr` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error: aborting due to previous error

For more information about this error, try `rustc --explain E0596`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

(playground)

The code can be fixed by making r mut and taking a &mut to r instead of a &.

This observation bewilders me, a Rust beginner. In my understanding, the result of dereferencing a & can be used in the some ways as a non-mut variable can. Is this somehow related to reborrowing? Can you please indicate the errors in my thought process and help me understand? Thank you.

To push something to a Vec<i32>, you must call Vec::push, which has the following signature:

pub fn push(&mut self, value: T)

The &mut self here means that to call it, you need an &mut Vec<i32>. Now, if your variable already has type &mut Vec<i32>, well then all is good, because you've got what you need.

On the other hand, if your variable has type Vec<i32>, then you need to first create a &mut Vec<i32> that points to your vector variable, however to create a mutable reference to a variable, the variable itself must be marked mut.

And finally, if you have an &&mut Vec<i32>, it can be turned into a &Vec<i32>, but not into a &mut Vec<i32>. Since you can't create an &mut Vec<i32>, you can't call push.

To illustrate this, you can write out the function calls fully:

let mut v = vec![];
let r = &mut v;

// desugared function calls
Vec::push(r, 0);
Vec::push(&mut v, 1);

In the first case, you can just pass it directly, but in the second case, you must create a mutable reference first.


What marking a variable with type &mut Vec<i32> as mut allows you to do, is to create a &mut &mut Vec<i32>.

1 Like

Thanks for the reply, Alice!

I still have some trouble understanding this part:

In my understanding, the auto-dereferencing rules transform

let mut v = vec![];
v.push(0);

to

let mut v = vec![];
(&mut v).push(0);

by adding an implicit &mut to match the signature of Vec::push.

Now, the auto-dereferencing rules also include automatically dereferencing to match signatures, so why is the compiler not allowed to turn

let mut v = vec![];
let r = &mut v;

let rr = &r;
rr.push(0);

to

let mut v = vec![];
let r = &mut v;

let rr = &r;
(*rr).push(0); // dereference &&mut once to get &mut

to create a &mut Vec<i32>? :thinking:

To turn a &T into a T with the dereference operator requires T to be Copy, but mutable references are not copy, so you can't do this for T = &mut Vec<i32>.

Another way to think of it is this: If you have a mutable reference to something, that means that you are the only one with access to that thing right now. If you have an immutable reference to something, that denotes shared access to the object. If you have access to the vector first through shared access, and then through a layer of exclusive access, then that only gives you shared access, thus you can't get a mutable reference to the vector.

2 Likes

Thank you so much Alice! You've resolved my confusion, which originates from not knowing that

The shared/exclusive access one also helped me a lot.

You may like this blog post that talks a bit more about the shared/exclusive idea.

1 Like

That blog post taught me the rationale for the differentiation between unique and shared access. Thank you again for your help!

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.