Rust 借用检查器仅在 returns 分配了具有相同生命周期的引用的函数时抱怨多次借用为可变

Rust Borrow checker only complains about borrowing as mutable multiple times when a function that returns a reference with the same lifetime assigned

我在使用一些 Rust 代码时遇到问题,在某些情况下我被允许多次借用可变的东西(第一个令人困惑的部分),但在其他情况下则不行。

我写了下面的例子来说明: (Playground)

struct NoLifetime {}
struct WithLifetime <'a> {
    pub field: &'a i32
}

fn main() {
    let mut some_val = NoLifetime {};
    borrow_mut_function(&mut some_val);
    borrow_mut_function(&mut some_val); // Borrowing as mutable for the second time.

    let num = 5;
    let mut life_val = WithLifetime { field: &num };
    borrow_lifetime(&mut life_val);
    borrow_lifetime(&mut life_val); // Borrowing as mutable for the second time.

    let num_again = borrow_lifetime(&mut life_val); // Borrow, assign lifetime result
    borrow_lifetime(&mut life_val); // Compiler: cannot borrow `life_val` as mutable more than once
}

fn borrow_mut_function(val_in: &mut NoLifetime) -> String {
    "abc".to_string()
}
fn borrow_lifetime<'a>(val_in: &'a mut WithLifetime) -> &'a i32 {
    val_in.field
}

如果你看到了,我可以多次借用 some_vallife_val 作为可变的。但是赋值了borrow_lifetime的return值后,我就不能再借了

我的问题如下:

  1. 来自 'The Rules' 关于 Rust Book 中的借用,我应该在 'exactly one mutable reference' 范围内使用相同的值。但是,在上面的代码中,每次我调用 borrow_ 函数时,我都将借用为可变的。
  2. 当我有一个return与参数具有相同生命周期的函数,并且我分配了那个参数时,为什么不允许相同类型的借用。

如有任何帮助,我们将不胜感激。我想这里发生的事情是我误解了 'borrowing as mutable' 的真正含义,以及何时确定某些东西被借用为可变的。

这是关于借用的范围,以及你是否保持借用。在上面的大部分调用中,some_val是在函数调用时借用的,但是在函数returns.

之后被return借用了。

例外情况:

let num_again = borrow_lifetime(&mut life_val); //Borrow, assign lifetime result

您在调用 borrow_lifetime 期间借用 life_val,但由于 return 值与参数 ('a) 具有相同的生命周期,借用的范围被扩展到包括 num_again 的生命周期,即直到函数结束。再次借用 life_val 是不安全的,因为 num_again 仍然是对它的引用。

,但我认为值得进一步解释。

在 Rust 中有 2 种转移所有权的方法:

  • 移动永久转移
  • 借用临时转让,预计归还

Rust 与许多其他语言一样,使用一堆 词法作用域 对时间流逝进行建模。因此,暂时,借用从创建它的地方开始,并延伸到其范围的末尾。


因此,借用何时结束的问题类似于询问在什么范围内创建借用。

让我们用带编号的行回顾一下您的示例:

fn main() {
    let mut some_val = NoLifetime {};                // 1
    borrow_mut_function(&mut some_val);              // 2
    borrow_mut_function(&mut some_val);              // 3
                                                     // 
    let num = 5;                                     // 4
    let mut life_val = WithLifetime { field: &num }; // 5
    borrow_lifetime(&mut life_val);                  // 6
    borrow_lifetime(&mut life_val);                  // 7
                                                     //
    let num_again = borrow_lifetime(&mut life_val);  // 8
    borrow_lifetime(&mut life_val);                  // 9
}

调用函数时,借用参数:

  • 至少在函数调用期间
  • 直到结果被丢弃的那一刻,如果结果与参数共享生命周期

所以,让我们看看这个:

  • 在第 (2) 和 (3) 行你调用 borrow_mut_function which returns a String: 结果不与参数共享任何生命周期,因此参数仅在函数调用的生命周期内借用。

  • 在第 (6) 和 (7) 行你调用 borrow_lifetime which returns a &'a i32: 结果与参数共享生命周期,所以参数被借用到结果范围的末尾......这是因为结果没有被使用。

  • 在第 (8) 行你调用 borrow_lifetime 其中 returns 一个 &'a i32 然后你 分配 结果给num_again:结果与参数共享一个生命周期,所以参数被借用到num_again.

  • 的范围结束
  • 在第 (9) 行你调用了 borrow_lifetime 但是它的参数仍然被 num_again 借用所以这个调用是非法的。

就是这样,这就是今天 Rust 的工作方式。


以后有电话non-lexical borrows。也就是说,编译器会意识到:

  • num_again 从未使用过
  • num_again 没有特定的析构函数(没有 Drop 实现)

因此可以决定它的借用结束早于词法范围的结束。