rust单挑_关于rust:如何同时获取对两个数组元素的可变引用?

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 {

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)

}

}

}

}

在夜间通道上,可以使用切片进行模式匹配。只要您没有巨大的索引并且在编译时就知道索引,就可以使用它。

#![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]);

}

请注意,您需要启用功能slice_patterns。

Rust的借用规则需要在编译时检查,这就是为什么像可变地借用Vec的一部分之类的问题很难解决(即使不是不可能)的问题,以及为什么Rust无法实现。

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

想象我做了类似的事情

let guard = something(&mut v[i]);

do_something_else(&mut v[j]);

guard.do_job();

在这里,我创建了一个对象guard,该对象在内部存储了对v[i]的可变引用,并在调用do_job()时对其进行了处理。

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

由于编译器不能保证i != j,因此被禁止。

这是一个简单的示例,但是类似的情况也很常见,这就是为什么这种访问方式会可变地借用整个容器的原因。加上编译器实际上对Vec的内部知识了解不足以确保即使i != j,此操作也是安全的这一事实。

在确切的情况下,您可以查看Vec上可用的swap(..)方法,该方法执行您正在手动实现的交换。

在更一般的情况下,您可能需要另一个容器。可能会将Vec的所有值包装为具有内部可变性的类型,例如Cell或RefCell,甚至使用完全不同的容器,如@llogiq在他对par-vec的回答中建议的那样。

方法[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,因为要更改矢量。一个不可变的版本将克隆并对其执行交换。

问题在于&mut v[…]首先可变地借用v,然后将可变引用该元素提供给change-function。

此reddit评论可以解决您的问题。

编辑:感谢您的单挑,Shepmaster。 par-vec是一个库,可以可变地借用vec的分离分区。

请内联解决方案。 随着时间的推移,链接会过时。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值