Multiple mutable references in a loop
Asked Answered
J

2

2

I'm trying to learn Rust's ownership and lifetimes in more detail, and I'm very confused by this piece of code:

let mut lst = vec![1, 2, 3];
let mut x = &mut 0;

for value in &mut lst {
    *value += 1;
    *x += 1;
    x = value;
}

*x += 1;
println!("{:?}", &lst);

From what I understand, Rust disallows having more than one mutable reference to any value, and a mutable reference to an element in a vector also borrows the vector itself. So it should not be possible to simultaneously have mutable references to two elements in a vector.

But in the code above, the body of the loop stores a mutable reference to an element in lst in x outside the loop. Then, in the next iteration, it takes another mutable reference to a different element in lst, giving me two mutable references to two elements in the list at once.

So my question is: Why is this allowed?

Juggle answered 12/10, 2021 at 18:55 Comment(1)
Not only can you get mutable references to two distinct elements at the same time. You can get mutable references to all elements at the same time: lst.iter_mut().collect::<Vec<_>>(). This works because each mutable reference from the iterator will be distinct. Much like you can simultaneously have mutable references to different fields in a struct.Imparity
A
7

While Rust does not allow you to have multiple mutable references to the same value, it does allow you to have mutable references to non-overlapping parts of the same value.

For example, you can split a &mut T into separate mutable references for each field, and the borrow checker will keep track of this for you:

struct Foo {
    x: i32,
    y: i32,
}

let mut foo = Foo { x: 0, y : 0 };

let foo_mut = &mut foo;
let x = &mut foo_mut.x;
let y = &mut foo_mut.y;

*x = 1;
*y = 2;

println!("x = {:?}", x);     // 1
println!("y = {:?}", y);     // 2
println!("foo = {:?}", foo); // Foo { x: 1, y: 2 }

Similarly, you can split a mutable slice reference &mut [T] with methods like split_at_mut and split_first_mut, which both give you two non-overlapping mutable references to the slice.

In fact, iteration of a mutable slice can be implemented using split_first_mut:

let mut lst = vec![1, 2, 3];
let mut x = &mut 0;

let mut rest = &mut *lst;
while let Some((value, rest_)) = rest.split_first_mut() {
    rest = rest_;

    *value += 1;
    *x += 1;
    x = value;
}

*x += 1;
println!("{:?}", &lst);

So it's fine for the iterator of &mut [T] (and &mut Vec<T>) to give multiple mutable references that coexist, as long as those mutable references point to different parts of the vector and do not overlap.

Aksoyn answered 12/10, 2021 at 19:16 Comment(4)
nice explanation :DMillner
@Mark You're absolutely correct, thanks!Aksoyn
what about non-overlapping parts of a mutable slice?Beleaguer
@GuerlandoOCs Yes, you are allowed to have multiple mutable references to non-overlapping parts of a slice, and you can construct those in safe Rust by using for example split_at_mut.Aksoyn
M
1

In the end you do not hold a mut reference more than once,in x = value the reference is moved into x. It would be different if that line was first in the loop:

fn main() {
    let mut lst = vec![1, 2, 3];
    let mut x = &mut 0;

    for value in &mut lst {
        x = value;

        *value += 1;
        *x += 1;
    }

    *x += 1;
    println!("{:?}", &lst);
}

In where of course the compiler complains:

error[E0503]: cannot use `*value` because it was mutably borrowed
 --> src/main.rs:8:9
  |
6 |         x = value;
  |             ----- borrow of `*value` occurs here
7 | 
8 |         *value += 1;
  |         ^^^^^^^^^^^ use of borrowed `*value`
9 |         *x += 1;
  |         ------- borrow later used here
Millner answered 12/10, 2021 at 19:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.