为什么基于 len() 索引一个可变向量被认为是同时借用?
Why is indexing a mutable vector based on its len() considered simultaneous borrowing?
我知道一般的答案——你只能可变地借用一次或不可变地借用多次,但不能同时借用。我想知道为什么这个具体案例被认为是同时借用。
我有以下代码:
fn main() {
let mut v = vec![1, 2, 3, 4, 5];
let n = 3;
// checks on n and v.len() and whatever else...
let mut s = v[..n].to_vec();
for i in 0..n {
v[i + v.len() - n] = s[1];
}
}
在 1.36.0 下会产生以下错误:
error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
--> src/main.rs:7:15
|
7 | v[i + v.len() - n] = s[1];
| ------^-----------
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
| mutable borrow later used here
似乎在计算 x
之前无法写入 v[x]
,届时不可变借用将完成。既然这里的顺序是完全连续的,为什么编译器不识别依赖关系并将它们视为非重叠借用?换句话说,这是否会导致实际问题?
,但我认为情况并非如此。如果有另一个线程具有(可能)可变引用,则调用 v.len()
或启动 v[...]
将是违规行为。在这里,编译器知道 v
发生的一切——它是一个本地定义,没有其他调用。对我来说,问题是为什么在 v[]
直到 len()
returns 之前都不可能发生这种同时借贷。它类似于 v.mutable_call(v.immutable_call());
顺便说一句,早期版本的编译器 (1.28) 给出了一个错误,指出右括号是可变借用的结尾,所以顺序似乎是基于源代码顺序的,因为源代码有两个混合在一起,它们可以被认为是重叠的。如果是这样,编译器肯定可以改进这一点......对吗?
这似乎与
密切相关
If so, surely the compiler could improve this...right?
确实,NLL 有意保守启动,如 #494341.
中所述
提取临时文件使其可以编译:
fn main() {
let mut v = vec![1, 2, 3, 4, 5];
let n = 3;
// checks on n and v.len() and whatever else...
let s = v[..n].to_vec();
for i in 0..n {
let index = i + v.len() - n;
v[index] = s[1];
}
}
这清楚地表明问题严格来说是在尝试使用索引之前没有计算索引。
由于无法在计算 Idx
之前启动对 IndexMut<Idx>::index_mut(&mut self, index: Idx)
的调用,因此没有理由在计算索引之前启动 v
的可变借用。
1 由 trentcl 提供。
我知道一般的答案——你只能可变地借用一次或不可变地借用多次,但不能同时借用。我想知道为什么这个具体案例被认为是同时借用。
我有以下代码:
fn main() {
let mut v = vec![1, 2, 3, 4, 5];
let n = 3;
// checks on n and v.len() and whatever else...
let mut s = v[..n].to_vec();
for i in 0..n {
v[i + v.len() - n] = s[1];
}
}
在 1.36.0 下会产生以下错误:
error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
--> src/main.rs:7:15
|
7 | v[i + v.len() - n] = s[1];
| ------^-----------
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
| mutable borrow later used here
似乎在计算 x
之前无法写入 v[x]
,届时不可变借用将完成。既然这里的顺序是完全连续的,为什么编译器不识别依赖关系并将它们视为非重叠借用?换句话说,这是否会导致实际问题?
v.len()
或启动 v[...]
将是违规行为。在这里,编译器知道 v
发生的一切——它是一个本地定义,没有其他调用。对我来说,问题是为什么在 v[]
直到 len()
returns 之前都不可能发生这种同时借贷。它类似于 v.mutable_call(v.immutable_call());
顺便说一句,早期版本的编译器 (1.28) 给出了一个错误,指出右括号是可变借用的结尾,所以顺序似乎是基于源代码顺序的,因为源代码有两个混合在一起,它们可以被认为是重叠的。如果是这样,编译器肯定可以改进这一点......对吗?
这似乎与
If so, surely the compiler could improve this...right?
确实,NLL 有意保守启动,如 #494341.
中所述提取临时文件使其可以编译:
fn main() {
let mut v = vec![1, 2, 3, 4, 5];
let n = 3;
// checks on n and v.len() and whatever else...
let s = v[..n].to_vec();
for i in 0..n {
let index = i + v.len() - n;
v[index] = s[1];
}
}
这清楚地表明问题严格来说是在尝试使用索引之前没有计算索引。
由于无法在计算 Idx
之前启动对 IndexMut<Idx>::index_mut(&mut self, index: Idx)
的调用,因此没有理由在计算索引之前启动 v
的可变借用。
1 由 trentcl 提供。