"cannot borrow as immutable because it is also borrowed as mutable" 在嵌套数组索引中意味着什么?
What does "cannot borrow as immutable because it is also borrowed as mutable" mean in an nested array index?
在这种情况下错误是什么意思:
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
我发现索引是通过 Index
和 IndexMut
特性实现的,并且 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;
}
令我惊讶的是,这完美无缺!为什么第一个片段不起作用,但第二个片段起作用?按照我对文档的理解,它们应该是等价的,但显然不是这样。
脱糖版与您的略有不同。行
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)
首先求值。这导致usize
,v
的临时共享借用可以再次释放。然后,评估 index_mut()
的接收者,即 v
是可变借用的。这工作正常,因为共享借用已经完成,并且整个表达式通过了借用检查器。
请注意,自引入 "non-lexical lifetimes" 以来,编译的版本只会这样做。在 Rust 的早期版本中,共享借用会一直存在到表达式结束并导致类似的错误。
我认为最干净的解决方案是使用临时变量:
let i = v[1];
v[i] = 999;
在这种情况下错误是什么意思:
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
我发现索引是通过 Index
和 IndexMut
特性实现的,并且 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;
}
令我惊讶的是,这完美无缺!为什么第一个片段不起作用,但第二个片段起作用?按照我对文档的理解,它们应该是等价的,但显然不是这样。
脱糖版与您的略有不同。行
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)
首先求值。这导致usize
,v
的临时共享借用可以再次释放。然后,评估 index_mut()
的接收者,即 v
是可变借用的。这工作正常,因为共享借用已经完成,并且整个表达式通过了借用检查器。
请注意,自引入 "non-lexical lifetimes" 以来,编译的版本只会这样做。在 Rust 的早期版本中,共享借用会一直存在到表达式结束并导致类似的错误。
我认为最干净的解决方案是使用临时变量:
let i = v[1];
v[i] = 999;