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()
:
let mut v = vec![1, 2, 3]; let (a, b) = v.split_at_mut(1); // Returns (&mut [1], &mut [2, 3]) change(&mut a[0], &mut b[0]);
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 an unsafe
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).
enum Pair<T> { Both(T, T), One(T), None, } fn index_twice<T>(slc: &mut [T], a: usize, b: usize) -> Pair<&mut T> { if a == b { slc.get_mut(a).map_or(Pair::None, Pair::One) } else { if a >= slc.len() || b >= slc.len() { Pair::None } else { // safe because a, b are in bounds and distinct unsafe { let ar = &mut *(slc.get_unchecked_mut(a) as *mut _); let br = &mut *(slc.get_unchecked_mut(b) as *mut _); Pair::Both(ar, br) } } } }
Since Rust 1.26, pattern matching can be done on slices. You can use that as long as you don't have huge indices and your indices are known at compile-time.
fn change(a: &mut i32, b: &mut i32) {
let c = *a;
*a = *b;
*b = c;
}
fn main() {
let mut arr = [5, 6, 7, 8];
{
let [ref mut a, _, ref mut b, ..] = arr;
change(a, b);
}
assert_eq!(arr, [7, 6, 5, 8]);
}
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
let guard = something(&mut v[i]);
do_something_else(&mut v[j]);
guard.do_job();
Here, I create an object guard
that internally stores a mutable reference to v[i]
, and will do something with it when I call do_job()
.
In the meantime, I did something that changed v[j]
. guard
holds a mutable reference that is supposed to guarantee nothing else can modify v[i]
. In this case, all is good, as long as i
is different from j
; 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 if i != j
.
In your precise case, you can have a look at the swap(..)
method available on Vec
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 as Cell
or RefCell
, or even using a completely different container, as @llogiq suggested in his answer with par-vec
.
The method [T]::iter_mut()
returns an iterator that can yield a mutable reference for each element in the slice. Other collections have an iter_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:
pub trait SliceExt {
type Item;
fn get_two_mut(&mut self, index0: usize, index1: usize) -> (&mut Self::Item, &mut Self::Item);
}
impl<T> SliceExt for [T] {
type Item = T;
fn get_two_mut(&mut self, index0: usize, index1: usize) -> (&mut Self::Item, &mut Self::Item) {
match index0.cmp(&index1) {
Ordering::Less => {
let mut iter = self.iter_mut();
let item0 = iter.nth(index0).unwrap();
let item1 = iter.nth(index1 - index0 - 1).unwrap();
(item0, item1)
}
Ordering::Equal => panic!("[T]::get_two_mut(): received same index twice ({})", index0),
Ordering::Greater => {
let mut iter = self.iter_mut();
let item1 = iter.nth(index1).unwrap();
let item0 = iter.nth(index0 - index1 - 1).unwrap();
(item0, item1)
}
}
}
}
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]
and v[1]
are clearly separate chunks, that doesn't stand to serious scrutiny. What if v
is some kind of map called NullMap
that maps all elements to a single field? How will compiler know in a Vec
operationsv[0];v[1];
is safe but in NullMap
isn't?
If you are trying to swap two elements of an array, why not go for slice::swap
?
fn main() {
let mut v = vec![1, 2, 3];
v.swap(0,1);
println!("{:?}",v);
}
Also v
needs to be mut
, because you are changing vector. An immutable version would clone and perform a swap on it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With