如果 let borrow 在 return 之后即使有 #![feature(nll​​)]

If let borrow stays after return even with #![feature(nll)]

我正在处理一个大文件,但这是一个导致同样问题的小玩具示例。抱歉,如果示例本身没有意义。

#![feature(nll)]
struct S(i32);

impl S {
    fn foo(&mut self) -> Option<&i32> {
        if let Some(val) = self.bar() {
            return Some(val);
        }
        let y = &mut self.0;
        None
    }

    fn bar(&mut self) -> Option<&i32> {
        None
    }
}

fn main() {
    S(0).foo();
}

这没有通过借阅检查器:

error[E0499]: cannot borrow `self.0` as mutable more than once at a time
 --> test.rs:9:17
  |
6 |         if let Some(val) = self.bar() {
  |                            ---- first mutable borrow occurs here
...
9 |         let y = &mut self.0;
  |                 ^^^^^^^^^^^ second mutable borrow occurs here
  |
note: first borrowed value must be valid for the anonymous lifetime #1 defined on the method body at 5:5...
 --> test.rs:5:5
  |
5 | /     fn foo(&mut self) -> Option<&i32> {
6 | |         if let Some(val) = self.bar() {
7 | |             return Some(val);
8 | |         }
9 | |         let y = &mut self.0;
10| |         None
11| |     }
  | |_____^

这不应该是有效的(即使没有 #![feature(nll)])因为它在 if let 块中返回吗?值得注意的是,如果我将 if let 块更改为以下内容,它编译得很好

if self.bar().is_some() {
    return self.bar();                                                                                      
}

让我们在这里详细了解一下生命周期。函数 foo() 被脱糖为

fn foo<'a>(&'a mut self) -> Option<&'a i32>

即returned 值最多存在 selfbar().

也类似

foo()中,行

if let Some(val) = self.bar() {

借用了 self 的生命周期 'b,returned 引用 val 也有此生命周期 'b。既然你那么returnSome(val),生命周期'b肯定比self参数foo()的生命周期'a长,肯定比foo() 的运行时间。这意味着您以后无法在 foo().

中再次借用 self

我认为这个例子中令人惊讶的是 self 的借位甚至发生在 bar() returns None 的情况下。直觉上,我们觉得在这种情况下没有引用 returned,所以我们不需要借用。然而,Rust 中的生命周期是由类型检查器检查的,而类型检查器不理解类型的不同值的含义。由 bar() 编辑的值 return 具有类型 Option<&'b i32>,无论它是 returns None 还是 Some,并且生命周期 'b 必须至少与 'a 一样长 – 鉴于限制,没有其他解决方案,因此借用检查器必须拒绝这个。

对于非词法生命周期,编译器可以引入更灵活的生命周期,这些生命周期不受词法作用域的约束,并且可以以以前不可能的方式重叠。但是,如果根本没有满足所有约束的生命周期,NLL 将无济于事。

您给出的最后一个代码片段完全不同。让我们添加生命周期:

if self.bar<'b>().is_some() {
    return self.bar<'c>();                                                                                      
}

现在我们调用 bar() 两次,每次调用都可以有不同的生命周期。现在只有生命周期 'c 需要比 'a 长,但生命周期 'b 只需要足够长以对结果调用 is_some()。生命周期为 'c 的借用只在分支被取时发生,不会发生冲突。