嵌套数组索引中的“不能借为不可变的,因为它也被借为可变的”是什么意思?


16

在这种情况下,错误是什么意思:

fn main() {
    let mut v: Vec<usize> = vec![1, 2, 3, 4, 5];
    v[v[1]] = 999;
}
error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
 --> src/main.rs:3:7
  |
3 |     v[v[1]] = 999;
  |     --^----
  |     | |
  |     | immutable borrow occurs here
  |     mutable borrow occurs here
  |     mutable borrow later used here

我发现索引是通过IndexIndexMut特性实现的,这v[1]是的语法糖*v.index(1)。有了这些知识,我尝试运行以下代码:

use std::ops::{Index, IndexMut};

fn main() {
    let mut v: Vec<usize> = vec![1, 2, 3, 4, 5];
    *v.index_mut(*v.index(1)) = 999;
}

令我惊讶的是,这完美无缺!为什么第一个代码段不起作用,而第二个代码段却起作用?以我对文档的理解方式,它们应该是等效的,但事实并非如此。


2
通过代码出现来学习Rust?欢迎使用StackOverflow,并感谢您提出的重要问题!
Sven Marnach

精确地; )这是我这样做的第三年(在此之前是Haskell的2倍)〜>自从我开始对低级内容开始更加感兴趣以来,我就
想让

@LucasBoucke有趣的是,我通常在项目中使用Rust,但是我在Haskell中编写了此AoC。他们俩都是各自领域的杰出语言。
Boiethios

Answers:


16

脱糖版本与您所拥有的版本略有不同。线

v[v[1]] = 999;

实际上对

*IndexMut::index_mut(&mut v, *Index::index(&v, 1)) = 999;

这将导致出现相同的错误消息,但是注释会提示正在发生的事情:

error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
 --> src/main.rs:7:48
  |
7 |     *IndexMut::index_mut(&mut v, *Index::index(&v, 1)) = 999;
  |      ------------------- ------                ^^ immutable borrow occurs here
  |      |                   |
  |      |                   mutable borrow occurs here
  |      mutable borrow later used by call

与已简化版本的重要区别在于评估顺序。在实际进行函数调用之前,将按列出的顺序从左到右评估函数调用的参数。在这种情况下,这意味着先&mut v评估后,可变地借入v。接下来,Index::index(&v, 1)应该进行评估,但这是不可能的– v已经被可变地借用了。最后,编译器显示对函数的调用仍然需要可变引用index_mut(),因此,当尝试共享引用时,可变引用仍然有效。

实际编译的版本的评估顺序略有不同。

*v.index_mut(*v.index(1)) = 999;

首先,从左到右评估方法调用的函数参数,即*v.index(1)先评估。结果为usizev可以再次释放的临时共享借用。然后,对的接收者进行index_mut()评估,即被v可变地借用。这很好,因为共享借用已经完成,并且整个表达式都通过了借用检查器。

请注意,自从引入“非词法生存期”以来,编译的版本才这样做。在Rust的早期版本中,共享借用将一直存在到表达式结尾,并导致类似的错误。

我认为最干净的解决方案是使用一个临时变量:

let i = v[1];
v[i] = 999;

哇!这里有很多事情!感谢您抽出宝贵的时间来解释!(有趣的是,这些“怪癖”使语言对我来说更有趣...)。您是否也可以给出一个提示,为什么会*v.index_mut(*v.index_mut(1)) = 999;失败,因为“不能多次借用可变的v”〜>编译器不应该那样做,因为*v.index_mut(*v.index(1)) = 999;能够确定不再需要内部借用了吗?
卢卡斯·布克

@LucasBoucke Rust确实有一些古怪之处,有时有些不便,但在大多数情况下,这种情况下的解决方案非常简单。该代码仍然可读性强,与您最初的代码只有一点点差异,因此在实践中没什么大不了的。
Sven Marnach

@LucasBoucke对不起,到目前为止,我还没有看到您的修改。结果*v.index(1)是存储在该索引处的,并且该值不需要保持借用v。结果*v.index_mut(1),在另一方面,是一个可变的地方表达,可能在理论上被分配到,所以它保持借活着。从表面上看,应该有可能告诉借阅检查器将值表达式上下文中的位置表达式视为值表达式,因此这可能会在将来的Rust版本中进行编译。
Sven Marnach

RFC如何将其解糖到:{ let index = *Index::index(&v, 1); let value = 999; *IndexMut::index_mut(&mut v, index) = value; }
Boiethios

@FrenchBoiethios我不知道您如何将其正式化,而且我敢肯定它永远不会飞。如果您想解决这个问题,我看到的唯一方法就是改进借阅检查器,例如,使它检测到可变的借用可以在以后的时间开始,因为实际上并不需要那么早。(这个特定的想法可能也不起作用。)
Sven Marnach
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.