可变参考生命周期与不可变参考生命周期

时间:2018-04-29 19:39:03

标签: reference rust immutability mutable lifetime

我有以下代码:

struct Bar<T> {
    k: [T; 10],
}

impl<T> Bar<T> {
    fn thing(&self, i: usize) -> &T {
        &self.k[i]
    }

    fn thing_mut(&mut self, i: usize) -> &mut T {
        &mut self.k[i]
    }
}

struct Foo<'a, T: 'a> {
    bar: &'a Bar<T>,
    count: usize,
}

impl<'a, T> Foo<'a, T> {
    fn get(&mut self) -> Option<&'a T> {
        if self.count < 10 {
            let thing = self.bar.thing(self.count);
            self.count += 1;
            Some(thing)
        } else {
            None
        }
    }
}

struct FooMut<'a, T: 'a> {
    bar: &'a mut Bar<T>,
    count: usize,
}

impl<'a, T> FooMut<'a, T> {
    fn get(&mut self) -> Option<&'a mut T> {
        if self.count < 10 {
            let thing = self.bar.thing_mut(self.count);
            self.count += 1;
            Some(thing)
        } else {
            None
        }
    }
}

Rust playground

Foo编译,但FooMut不会:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/main.rs:40:34
   |
40 |             let thing = self.bar.thing_mut(self.count);
   |                                  ^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 38:5...
  --> src/main.rs:38:5
   |
38 | /     fn get(&mut self) -> Option<&'a mut T> {
39 | |         if self.count < 10 {
40 | |             let thing = self.bar.thing_mut(self.count);
41 | |             self.count += 1;
...  |
45 | |         }
46 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:40:25
   |
40 |             let thing = self.bar.thing_mut(self.count);
   |                         ^^^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 37:1...
  --> src/main.rs:37:1
   |
37 | impl<'a, T> FooMut<'a, T> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: ...so that the expression is assignable:
           expected std::option::Option<&'a mut T>
              found std::option::Option<&mut T>

为什么不可变的编译得很好,但不是可变的?我在FooMut案例中缺少一些终身注释吗?我已经看到了很多关于生命和引用的答案,但我在这个案例中特别询问了可变和不可变的案例。

1 个答案:

答案 0 :(得分:0)

不可变和可变引用的生命周期方面已在varius地方涵盖: 请参阅答案中嵌入的问题和参考文献的评论。

我在这里写了一些关注这个特定案例的笔记,希望能够揭示Rust生命周期的困难概念 (至少对我来说很难)。

考虑这个片段,这个简化版本暴露了同样的问题:

struct Foo<'a> {
    x: &'a mut i32,
}

impl<'b> Foo<'b> {
    fn x(&mut self) -> &'b mut i32 { self.x }
}

fn main() {
    let y = &mut 5;              // <- 'a(1)
    let mut f = Foo { x: y };    //    'a(1) <- 'b(2)
    println!("x is: {}", f.x()); //    'a(1)    'b(2) <- 'anonymous(3)
}

这里有三个生命周期:

  • 'a(1)生命周期为&mut i32
  • 类型的y值
  • 'b(2)生命周期为Foo
  • 类型的f值
  • 'anonymous(3)生命周期由编译器分配给&self引用,因为在&self方法中没有为fn x(&mut self) -> &'b i32分配明确的生命周期值。

在文档中,structimpl上的终身泛型通常使用相同的字母进行注释: 在此示例中,我使用struct注释了'a生命周期泛型,并使用impl注释了'b,以证明编译器生成的具体生命周期与两个不同的范围相关联。

请参阅上面示例代码中的注释以获得可视化表示。

如果我们尝试编译,我们得到:

error[E0312]: lifetime of reference outlives lifetime of borrowed content...
 --> src/main.rs:6:30
  |
6 |     fn x(&self) -> &'b i32 { self.x }
  |                              ^^^^^^
  |
note: ...the reference is valid for the lifetime 'b as defined on the impl at 5:1...
 --> src/main.rs:5:1
  |
5 | impl<'b> Foo<'b> {
  | ^^^^^^^^^^^^^^^^
note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on     the method body at 6:5
 --> src/main.rs:6:5
  |
6 |     fn x(&self) -> &'b i32 { self.x }
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

我们发现'anonymous生命周期比'b更窄(参见&#34; approssimate&#34;代码注释中的生命周期可视化):借来的内容self.x不够活跃满足防锈安全规则。

现在很明显,解决方案应该是在elision规则的支持下,通过明确的注释或更好的方式缩短生命周期:

struct Foo<'a> {
    x: &'a mut i32,
}

impl<'b> Foo<'b> {
    fn x(&mut self) -> &mut i32 { self.x }
}

fn main() {
    let y = &mut 5;              // <- 'a
    let mut f = Foo { x: y };        //    'a <- 'b
    println!("x is: {}", f.x()); //    'a    'b
}

现在该代码段已编译,此处学到的教训应该是从中复制的短语 这个great answer

  

经验法则:不要在任何地方发送垃圾邮件。对于应该相同的事物只使用相同的生命周期

不可变参考

好的,但为什么如果Foo::x是不可变引用,编译器不会抛出错误?

简短的回答是:

如果内部引用是不可变的,则编译器可以确保不会出现因缩小生命周期范围而导致的内存问题。

如果self.x&'a mut i32引用,并且编译器为了'anonymous分配了一个&self生命周期,则编译失败,因为引用的引用要求严格等于生命期在内部可变性的情况下,并且编译器'b'anonymous不等于。

有关详细信息,请参阅this great answer