【问题标题】:What does "cannot borrow as immutable because it is also borrowed as mutable" mean in an nested array index?嵌套数组索引中的“不能作为不可变借用,因为它也作为可变借用”是什么意思?
【发布时间】:2020-03-27 11:07:59
【问题描述】:

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

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;
}

令我惊讶的是,这完美无缺!为什么第一个 sn-p 不起作用,但第二个起作用?我理解文档的方式,它们应该是等价的,但显然不是这样。

【问题讨论】:

  • 随着代码的出现学习 Rust?欢迎来到 StackOverflow,感谢您提出的好问题!
  • 准确地说; ) 这是我做这件事的第三年(在那之前是 2x Haskell)~> 因为我开始对低级的东西更感兴趣,所以我想给 Rust 一个旋转
  • @LucasBoucke 这很有趣,我通常在我的项目中使用 Rust,但我在 Haskell 中编写了这个 AoC。它们都是各自领域的优秀语言。

标签: rust borrow-checker


【解决方案1】:

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

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

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

实际编译的版本的计算顺序略有不同。

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

首先,方法调用的函数参数从左到右计算,即首先计算*v.index(1)。这会产生usize,并且可以再次释放v 的临时共享借用。然后,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 确实有一些怪癖,有时有点不方便,但在大多数情况下,解决方案相当简单,就像在这种情况下一样。代码仍然非常可读,只是与您原来的代码略有不同,因此在实践中这没什么大不了的。
  • @LucasBoucke 抱歉,我直到现在才看到您的编辑。 *v.index(1) 的结果是存储在该索引处的 ,并且该值不需要保持 v 的借用活动。另一方面,*v.index_mut(1) 的结果是一个可变位置表达式,理论上可以分配给它,因此它确实使借用保持有效。从表面上看,应该可以教会借用检查器,值表达式上下文中的位置表达式可以被视为值表达式,因此这可能会在未来的 Rust 版本中编译。
  • 如何通过 RFC 将其脱糖:{ let index = *Index::index(&amp;v, 1); let value = 999; *IndexMut::index_mut(&amp;mut v, index) = value; }
  • @FrenchBoiethios 我不知道你是怎么把它正式化的,我敢肯定它永远不会飞。如果你想解决这个问题,我看到的唯一方法是改进借用检查器,例如让它检测到可变借用可以在以后开始,因为它并不那么早就需要。 (这个特别的想法可能也行不通。)
猜你喜欢
  • 1970-01-01
  • 2017-05-02
  • 1970-01-01
  • 2016-07-30
  • 2015-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-09
相关资源
最近更新 更多