为什么基于len()为可变向量编制索引被认为是同时借用的?

jsp*_*cer 5 rust borrow-checker

我知道一般的答案-您只能一次或多次多次可变借贷,但不能两次都借。我想知道为什么将这种特定情况视为同时借款。

我有以下代码:

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];
    }
}
Run Code Online (Sandbox Code Playgroud)

在1.36.0下会产生以下错误:

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];
    }
}
Run Code Online (Sandbox Code Playgroud)

似乎没有办法进行写操作v[x],直到x计算完为止,到那时不可变的借位将完成。由于此处的顺序完全是连续的,因此编译器为什么不识别依赖项,并将其视为非重叠借项?换句话说,是否存在任何可能导致实际问题的情况?

Marouane Fazouane建议使用并发作为一种可能性,但我认为情况并非如此。如果存在另一个具有(可能是)可变引用的线程,则调用then v.len()或start 将会违反v[...]。在这里,编译器知道发生的一切v-这是本地定义,没有其他调用。对我来说,问题是,为什么v[]len()归还之前根本没有办法进行这种同时借贷。类似于v.mutable_call(v.immutable_call());

顺便说一句,较早版本的编译器(1.28)给出了一个错误,指示在可变借位的结尾处使用了右括号,因此似乎顺序是基于源顺序的,并且由于源将两者混合在一起,因此它们可能是被认为是重叠的。如果是这样,肯定编译器可以改善这一点...对吗?

这似乎与为什么在没有借贷重叠发生时为什么会有借贷错误密切相关

Mat*_* M. 5

如果是这样,肯定编译器可以改善这一点...对吗?

实际上,NLL有意保守地开始,如#49434 1中所述

提取临时文件可以对其进行编译:

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];
    }
}
Run Code Online (Sandbox Code Playgroud)

这清楚地表明,问题完全是在尝试使用索引之前不计算索引的问题之一。

由于无法IndexMut<Idx>::index_mut(&mut self, index: Idx)在计算之前开始对的调用Idx,因此没有理由v在计算索引之前开始对其的可变借用。

1 trentcl提供