fn change(a: &mut i32, b: &mut i32) {
let c = *a;
*a = *b;
*b = c;
}
fn main() {
let mut v = vec![1, 2, 3];
change(&mut v[0], &mut v[1]);
}
When I compile the code above, it has the error:
error[E0499]: cannot borrow `v` as mutable more than once at a time
--> src/main.rs:9:32
|
9 | change(&mut v[0], &mut v[1]);
| - ^ - first borrow ends here
| | |
| | second mutable borrow occurs here
| first mutable borrow occurs here
Why does the compiler prohibit it? v[0]
and v[1]
occupy different memory positions, so it's not dangerous to use these together. And what should I do if I come across this problem?
You can solve this with
split_at_mut()
:There are uncountably many safe things to do that the compiler unfortunately does not recognize yet.
split_at_mut()
is just like that, a safe abstraction implemented with anunsafe
block internally.We can do that too, for this problem. The following is something I use in code where I need to separate all three cases anyway (I: Index out of bounds, II: Indices equal, III: Separate indices).
The method
[T]::iter_mut()
returns an iterator that can yield a mutable reference for each element in the slice. Other collections have aniter_mut
method too. These methods often encapsulate unsafe code, but their interface is totally safe.Here's a general purpose extension trait that adds a method on slices that returns mutable references to two distinct items by index:
You can't make two mutable references to the same data. This is something explicitly forbidden by the borrow checker, to prevent concurrent modifications. However you can bypass the borrow checker by using
unsafe
blocks.While in your case
v[0]
andv[1]
are clearly separate chunks, that doesn't stand to serious scrutiny. What ifv
is some kind of map calledNullMap
that maps all elements to a single field? How will compiler know in aVec
operationsv[0];v[1];
is safe but inNullMap
isn't?If you are trying to swap two elements of an array, why not go for
slice::swap
?Also
v
needs to bemut
, because you are changing vector. An immutable version would clone and perform a swap on it.On the nightly channel, pattern matching can be done with slices. You can use that as long as you don't have huge indices and your indices are known at compile-time.
Note that you need to enable the feature
slice_patterns
.The borrow rules of Rust need to be checked at compilation time, that is why something like mutably borrowing a part of a
Vec
is a very hard problem to solve (if not impossible), and why it is not possible with Rust.Thus, when you do something like
&mut v[i]
, it will mutably borrow the entire vector.Imagine I did something like
Here, I create an object
guard
that internally stores a mutable reference tov[i]
, and will do something with it when I calldo_job()
.In the meantime, I did something that changed
v[j]
.guard
holds a mutable reference that is supposed to guarantee nothing else can modifyv[i]
. In this case, all is good, as long asi
is different fromj
; if the two values are equal it is a huge violation of the borrow rules.As the compiler cannot guarantee that
i != j
, it is thus forbidden.This was a simple example, but similar cases are legions, and are why such access mutably borrows the whole container. Plus the fact that the compiler actually does not know enough about the internals of
Vec
to ensure that this operation is safe even ifi != j
.In your precise case, you can have a look at the
swap(..)
method available onVec
that does the swap you are manually implementing.On a more generic case, you'll probably need an other container. Possibilities are wrapping all the values of your
Vec
into a type with interior mutability, such asCell
orRefCell
, or even using a completely different container, as @llogiq suggested in his answer withpar-vec
.The problem is that
&mut v[…]
first mutably borrowsv
and then gives the mutable reference to the element to the change-function.This reddit comment has a solution to your problem.
Edit: Thanks for the heads-up, Shepmaster. par-vec is a library that allows to mutably borrow disjunct partitions of a vec.