Rust Borrow检查器只在函数返回具有相同生命周期的引用时多次抱怨借用为可变的

kpbwa7wx  于 2023-05-22  发布在  其他
关注(0)|答案(2)|浏览(133)

我在一些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的返回值后,我就不能再借了。
我的问题如下:
1.从Rust Book中关于借用的“规则”中,我应该在作用域中有“一个可变引用”,指向相同的值。然而,在上面的代码中,我在每次调用borrow_函数时都借用了可变的。
1.当我有一个函数,它返回与参数相同的生存期,并且我给参数赋值时,为什么不允许相同类型的借用呢?
任何帮助将不胜感激。我想这里发生的事情是我误解了“借用为可变的”的真正含义,以及何时确定某个东西是作为可变的被借用的。

rkkpypqq

rkkpypqq1#

由于non-lexical borrows,Rust现在接受了这段代码。
对于非词法借用,借用在变量的最后一次使用时结束(本质上),注意析构函数是一次使用,析构函数在作用域的末尾运行。
因此,当编译器意识到:

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

它决定它的borrow在它开始的那一行结束,而不是在词法作用域的末尾,因此life_val可以自由地再次使用。
Chris already gave the gist of it,但我认为值得进一步解释。
在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,它返回一个String:结果不与参数共享任何生存期,因此参数仅在函数调用的生存期内被借用。
  • 在第(6)行和第(7)行调用borrow_lifetime,它返回一个&'a i32:结果与参数共享生存期,因此参数被借用,直到结果的作用域结束……这是立即的,因为结果没有被使用。
  • 在第(8)行,你调用borrow_lifetime,它返回一个&'a i32,你把结果 * 赋值 * 给num_again:结果与参数共享生存期,因此该参数被借用,直到num_again作用域结束。
  • 在第(9)行调用borrow_lifetime,但是它的参数仍然被num_again借用,所以调用是非法的。

这就是Rust当时的工作方式。

f5emj3cl

f5emj3cl2#

这是关于借用的范围,以及您是否保持借用有效。在上面的大多数调用中,some_val在函数调用期间被借用,但在函数返回时被返回。
在例外情况下:

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

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

相关问题