3
votes

I just started out with Rust. I have some trouble understanding mutable and immutable borrows with respect to this snippet of code. Here is the code I am testing.

fn main() {                                                                                                   
    let mut list = vec![1,2,3,4,5,6];                                                                         
    let i = 2;                                                                                                
    list[list[i]] = list[list[i-1]];                                                                          
}

On compiling this, I get the following error:

error[E0502]: cannot borrow `list` as immutable because it is also borrowed as mutable
 --> src/main.rs:4:7
  |
4 |     list[list[i]] = list[list[i-1]];
  |     -----^^^^----
  |     |    |
  |     |    immutable borrow occurs here
  |     mutable borrow occurs here
  |     mutable borrow later used here

error: aborting due to previous error

When I replace the snippet above with this code instead,

fn main() {                                                                                                   
    let mut list = vec![1,2,3,4,5,6];                                                                         
    let i = 2;                                                                                                
    let index = list[i];                                                                                      
    list[index] = list[list[i-1]];                                                                            
}

the code compiles and executes fine.

My question is why is the compiler not flagging this as an error like this?

5 |     list[index] = list[list[i-1]];
  |     --------------|    |
  |     |             |    immutable borrow occurs here
  |     |             immutable borrow occurs here
  |     mutable borrow occurs here
  |     mutable borrow later used here

Isin't the right hand side of the assignment an immutable borrow? Why only flag the first code snippet as an error?

Does Rust have something like happens-before or sequenced before relationship in C++ which ensures that the immutable borrow on the right hand side is sequenced before the mutable borrow on the left hand side and hence it is not an error?

If the answer to the previous question is yes, then why can't the compiler sequence list[list[i]] internally as let tmp = list[i]; list[tmp]=...?

1
To those looking to answer, this actually is puzzling. Consider that list[list[0]] = 1 doesn't compile but *list.index_mut(*list.index(0)) = 1; does. - orlp
Can't write an answer now but I believe this has to do with the limitations of two-phase borrows. The fact that slice indexing doesn't go through the Index(Mut) traits, but is built in to the compiler, may also be relevant - that should usually be unobservable but I think it can make a difference in certain obscure cases like this - trentcl

1 Answers

0
votes

Here is some code that demonstrates the same issue, but a bit simplified.

fn main() {
    let mut list: Vec<usize> = vec![1,2,3,4,5,6];   
    let i: usize = 2;
    let place: &mut usize = &mut list[list[i]];
    *place = 3;
}

Evidently, the compiler is doing something like this:

fn main() {
    let mut list: Vec<usize> = vec![1,2,3,4,5,6];   
    let i: usize = 2;
    // mutable borrow of list starts
    let r: &mut Vec<usize> = &mut list;  
    // immutable borrow of list starts
    let q: usize = list[i];  
    // immutable borrow of list ends
    // mutable borrow of r starts
    let place: &mut usize = &mut r[q];  
    // place is a mutable borrow derived from r
    *place = 3;
    // place is dropped, mutable borrow of r ends
    // r is dropped, mutable borrow of list ends
}

This code works fine:

fn main() {
    let mut list: Vec<usize> = vec![1,2,3,4,5,6];   
    let i: usize = 2;
    let q: usize = list[i];               // immutable borrow begins and ends
    let place: &mut usize = &mut list[q]; // mutable borrow begins
    *place = 3;                           // mutable borrow ends
}