为什么基于 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 提供。