如何获得两个数组元素的可变引用同时?
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]); }
当我编译上面的代码时,它有错误:
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
为什么编译器禁止它? v[0]
和v[1]
占用不同的内存位置,所以一起使用并不危险。 如果遇到这个问题该怎么办?
你可以用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]);
编译器不幸的是,还有许多安全的事情要做。 split_at_mut()
就是这样,一个安全的抽象内部unsafe
块实现。
我们也可以这样做,这个问题。 以下是我在代码中使用的东西,无论如何我需要将所有三种情况分开(I:索引超出范围,II:指数相等,III:单独索引)。
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) } } } }
Rust的借用规则需要在编译时进行检查,这就是为什么像可变地借用Vec
的一部分是一个非常难以解决的问题(如果不是不可能的话),以及Rust为什么不可能。
因此,当你做像&mut v[i]
这样的东西时,它会可变地借用整个向量。
想象一下,我做了类似的事情
let guard = something(&mut v[i]); do_something_else(&mut v[j]); guard.do_job();
在这里,我创build了一个对象guard
,它在内部存储了一个对v[i]
的可变引用,当我调用do_job()
时候将会用它做一些事情。
与此同时,我做了一些改变v[j]
东西。 guard
持有一个可变的参考,应该保证没有别的可以修改v[i]
。 在这种情况下,只要i
与j
不同,这一切都是好的; 如果这两个值相等,这是对借贷规则的严重违反。
由于编译器不能保证i != j
,所以它是被禁止的。
这是一个简单的例子,但类似的情况是军团,这是为什么这种访问可变地借用整个容器。 再加上事实上编译器实际上对Vec
的内部知识不够了解,即使i != j
,也确保这个操作是安全的。
在你精确的情况下,你可以看看Vec
上的swap(..)
方法 ,它是你手动执行的交换。
在更通用的情况下,您可能需要另一个容器。 可能性是将Vec
所有值包装成内部可变性的types,比如Cell
或RefCell
,或者甚至使用完全不同的容器,就像@llogiq在par-vec
回答中所build议的那样。
在夜间频道上,可以用切片进行模式匹配。 只要你没有庞大的索引,你可以使用它,你的索引在编译时是已知的。
#![feature(slice_patterns)] 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 &mut [ref mut a, _, ref mut b, _..] = &mut arr; change(a, b); } assert_eq!(arr, [7, 6, 5, 8]); }
请注意,您需要启用functionslice_patterns
。
方法[T]::iter_mut()
返回一个迭代器,它可以为切片中的每个元素生成一个可变引用。 其他集合也有一个iter_mut
方法。 这些方法通常封装不安全的代码,但是它们的接口是完全安全的。
下面是一个通用扩展特性,它在片上添加一个方法,通过索引返回两个不同的项的可变引用:
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) } } } }
你不能对同一个数据做两个可变的引用。 这是借用检查程序明确禁止的,以防止同时修改。 但是,您可以通过使用unsafe
块来绕过借用检查器。
而在你的情况下, v[0]
和v[1]
显然是单独的块,这不是严格的审查。 如果v
是一种叫做NullMap
映射,它将所有元素映射到单个字段? 编译器如何知道Vec
操作v[0];v[1];
是安全的,但在NullMap
是不是?
如果你想交换数组中的两个元素,为什么不去slice::swap
?
fn main() { let mut v = vec![1, 2, 3]; v.swap(0,1); println!("{:?}",v); }
另外v
需要mut
,因为你正在改变vector。 一个不可变的版本会克隆并执行交换。
问题在于&mut v[…]
首先可变地借用v
,然后给元素的可变引用赋予变化函数。
这 reddit评论有解决您的问题。
编辑:感谢单挑,Shepmaster。 par-vec是一个允许可变地借用vec的不连续分区的库。