如果让借后还留,即使使用#也是如此!

时间:2018-10-28 18:26:37

标签: rust borrow-checker

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

#![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();                                                                                      
}

1 个答案:

答案 0 :(得分:3)

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

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

即返回值的生存期最长为self;对于bar()也是如此。

foo()中,该行

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

创建借用的self,该借用的生存期为'b,并且返回的引用val的生存期为'b。自从您返回Some(val)以来,生存期'b必须比'a参数的生存期selffoo()更长,这肯定比{ {1}}。这意味着您以后不能再在foo()中借用self

我认为在此示例中令人惊讶的是,即使foo()返回self也会发生bar()的借用。直觉上,在这种情况下,我们认为没有引用返回,因此我们不需要借用。但是,Rust中的生存期由类型检查器检查,并且类型检查器不理解类型的不同值的含义。 None返回的值的类型为bar(),而不管返回的是Option<&'b i32>还是None,并且生存期Some至少应与'b –鉴于约束,没有其他解决方案,因此借阅检查员必须拒绝这样做。

使用非词汇生存期,编译器可以引入更灵活的生存期,这些生存期不受词汇范围的约束,并且可以以以前无法实现的方式重叠。但是,如果没有寿命可以满足所有约束条件,那么NLL将无济于事。

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

'a

现在,我们两次呼叫if self.bar<'b>().is_some() { return self.bar<'c>(); } ,并且这些呼叫中的每一个都有不同的生存期。现在,只有生存期bar()才需要延长'c的寿命,但是生存期'a只需要足够长的时间就可以对结果调用'b。寿命为is_some()的借用仅在进行分支时发生,并且不会发生冲突。