Rust 如何同时获得对两个数组元素的可变引用?

Rust 如何同时获得对两个数组元素的可变引用?,rust,Rust,当我编译上面的代码时,它有一个错误: error[E0499]:一次不能将'v'作为可变项借用多次 -->src/main.rs:9:32 | 9 |变更(&mut v[0]、&mut v[1]); |-^-第一次借用到此结束 | | | ||第二个可变借用发生在此处 |第一个可变借用发生在这里 为什么编译器禁止它v[0]和v[1]占用不同的内存位置,因此将它们一起使用并不危险。如果遇到这个问题,我该怎么办?问题是&mut v[…]首先

当我编译上面的代码时,它有一个错误:

error[E0499]:一次不能将'v'作为可变项借用多次
-->src/main.rs:9:32
|
9 |变更(&mut v[0]、&mut v[1]);
|-^-第一次借用到此结束
|                     |          |
||第二个可变借用发生在此处
|第一个可变借用发生在这里

为什么编译器禁止它
v[0]
v[1]
占用不同的内存位置,因此将它们一起使用并不危险。如果遇到这个问题,我该怎么办?

问题是
&mut v[…]
首先可变地借用
v
,然后将元素的可变引用提供给change函数

reddit comment为您的问题提供了解决方案


编辑:谢普马斯特,谢谢你的提醒。是一个库,允许可变地借用vec的不相交分区。

Rust的借用规则需要在编译时进行检查,这就是为什么可变地借用vec的一部分是一个非常难解决的问题(如果不是不可能的话),以及为什么Rust不可能做到这一点

因此,当您执行类似于
&mut v[i]
的操作时,它将可变地借用整个向量

想象一下我做了类似的事情

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]);
}
在这里,我创建了一个对象
guard
,它在内部存储对
v[I]
的可变引用,并在调用
do_job()
时对其进行处理

与此同时,我做了一些改变
v[j]
guard
保存一个可变引用,该引用应该保证没有其他东西可以修改
v[i]
。在这种情况下,只要
i
j
不同,一切都是好的;如果这两个值相等,则严重违反了借用规则

因为编译器不能保证
i!=j
,因此是禁止的

这是一个简单的例子,但类似的案例很多,这就是为什么这样的访问会相互借用整个容器。加上编译器实际上对
Vec
的内部结构了解不够,因此即使
i!=j


在您的具体情况下,您可以查看
Vec
上提供的用于手动执行交换的功能

在更一般的情况下,您可能需要另一个容器。可能的做法是将您的
Vec
的所有值包装到一个具有内部易变性的类型中,例如or,或者甚至使用一个完全不同的容器,正如@llogiq在其答案中所建议的那样。

您可以通过以下方法解决此问题:

有无数安全的事情要做,不幸的是编译器还没有识别出来
split_at_mut()
就是这样,它是一个安全的抽象,在内部用
unsafe
块实现

对于这个问题,我们也可以这样做。下面是我在代码中使用的东西,我需要将所有三种情况分开(I:索引越界,II:索引相等,III:单独索引)

enum对{
两者(T,T),
一(T),
没有一个
}
fn index_两次(slc:&mut[T],a:usize,b:usize)->Pair{
如果a==b{
slc.get_mut(a).map_或(对::无,对::一)
}否则{
如果a>=slc.len(){
对::无
}否则{
//安全,因为a,b在界内且不同
不安全{
设ar=&mut*(slc.get_unchecked_mut(a)作为*mut u);
设br=&mut*(slc.get_unchecked_mut(b)为*mut u);
配对::两个(ar、br)
}
}
}
}

不能对同一数据进行两个可变引用。这是借用检查器明确禁止的,以防止并发修改。但是,您可以使用
不安全的
块绕过借用检查器

虽然在您的案例中,
v[0]
v[1]
显然是独立的块,但这不值得认真审查。如果
v
是一种称为
NullMap
的映射,它将所有元素映射到单个字段,该怎么办?编译器如何在
Vec
操作
v[0]中知道;v[1]是安全的,但在
NullMap
中不是吗


如果您试图交换数组的两个元素,为什么不使用


另外,
v
需要是
mut
,因为您正在更改向量。不可变版本将克隆并在其上执行交换。

由于Rust 1.26,模式匹配可以在片上完成。您可以使用它,只要您没有庞大的索引,并且您的索引在编译时是已知的

fn main() {
    let mut v = vec![1, 2, 3];
    v.swap(0,1);
    println!("{:?}",v);
}
该方法返回一个迭代器,该迭代器可以为切片中的每个元素生成可变引用。其他收藏也有
iter\u mut
方法。这些方法通常封装不安全的代码,但它们的接口是完全安全的

下面是一个通用扩展特性,它在片上添加了一个方法,该方法通过索引返回对两个不同项的可变引用:

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]);
}
pub文本{
类型项目;
fn get_two_mut(&mut self,index0:usize,index1:usize)->(&mut self::Item,&mut self::Item);
}
[T]的impl SliceExt{
类型项=T;
fn get_two_mut(&mut self,index0:usize,index1:usize)->(&mut self::Item,&mut self::Item){
匹配index0.cmp(&index1){
排序::Less=>{
设mut iter=self.iter_mut();
设item0=iter.nth(index0.unwrap();
设item1=iter.nth(index1-index0-1).unwrap();
(第0项、第1项)
}
排序::Equal=>panic!(“[T]::get_two_mut():两次收到相同的索引({})”,index0),
排序::更大=>{
设mut iter=self.iter_mut();
设item1=iter。
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)
            }
        }
    }
}
fn main() {
    let mut v = vec![1, 2, 3];
    v.swap(0,1);
    println!("{:?}",v);
}
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]);
}
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)
            }
        }
    }
}