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

时间:2016-06-24 05:52:43

标签: rust lifetime borrow-checker

我在某些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. 来自&#39;规则&#39;关于Rust Book中的借款,我应该只有一个可变的参考文献&#39;在范围内相同的值。但是,在上面的代码中,每当我调用borrow_函数时,我都会将其作为可变对象。
  2. 当我的函数返回与参数具有相同生命周期的函数时,为什么不允许相同类型的借用,并且我分配了该参数。
  3. 任何帮助将不胜感激。我想这里发生的事情是,我误解了什么是“可变的借款”。真的意味着,何时确定某些东西被借用为可变的。

2 个答案:

答案 0 :(得分:7)

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今天的工作方式。

将来需要non-lexical borrows。也就是说,编译器会意识到:

  • num_again从未使用
  • num_again没有特定的析构函数(没有Drop实现)
因此,可以决定其借款的结束时间早于词汇范围的结束。

答案 1 :(得分:5)

这是关于借款的范围,以及你是否保持借款。在上面的大多数调用中,some_val在函数调用期间被借用,但在函数返回后返回。

在例外情况下:

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

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