3

I have the following code:

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 compiles, but FooMut does not:

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>

Why does the immutable one compile just fine, but not the mutable one? Is there some lifetime annotation I am missing in the FooMut case? I have seen plenty of answers for lifetimes and references, but I am specifically asking in this case about the mutable vs non-mutable case.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
user1413793
  • 9,057
  • 7
  • 30
  • 42
  • 2
    I believe your question is already answered by [Why does linking lifetimes matter only with mutable references?](https://stackoverflow.com/q/32165917/155423). If not that, [How to resolve lifetime error for mutable reference in Rust?](https://stackoverflow.com/q/44081272/155423) or [Mutable versus immutable lifetime](https://stackoverflow.com/q/34568157/155423) are likely to answer it. Please [edit] your question to explain why it is different from these existing questions. If it's not, we can mark this as already answered. – Shepmaster Apr 29 '18 at 20:33

1 Answers1

1

The lifetime aspects of immutable and mutable references has already been covered in varius places: see comments to the question and references embedded in the answer.

I write here some notes focusing on this specific case with the hope to shed some light on the difficult concept of Rust lifetimes (At least for me it is difficult).

Consider this snippet, a simplified version that exposes the same problem of the question:

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)
}

Here there are three lifetime at play:

  • 'a(1) lifetime of y value of type &mut i32
  • 'b(2) lifetime of f value of type Foo
  • 'anonymous(3) lifetime assigned by the compiler to &self reference because no explicit lifetime value is assigned to &self in fn x(&mut self) -> &'b i32 method.

In the docs usually lifetime generics on struct and impl are annotated with the same letter: In this example I have annotated the struct lifetime generic with 'a and the impl with 'b to evidentiate that the concrete lifetimes generated by the compiler are associated to two different span.

See the comments in the example code above to get a visual representation.

If we try to compile we get:

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 }
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

We see that the 'anonymous lifetime is narrower that 'b (see "approssimate" lifetime visualization in code comment): the borrowed content self.x does not live enough to satisfy rust safety rules.

Now it is clear that the solution should be to reduce lifetimes with explicit annotation or better with the support of elision rule:

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
}

Now the snippet compiles, and the lesson learned here should be a phrase copied from this answer:

A rule of thumb: don't just spam a single lifetime everywhere. Only use the same lifetime for things that should be the same

Immutable reference

Ok, but why if Foo::x is an immutable reference the compiler does not throw an error?

The short answer is:

If the inner reference is immutable the compiler is assured that no memory issues can arise for narrowing the lifetime scope.

Instead compiler prevents different lifetimes span in case of inner mutability (in this case prevents 'anonymous != 'b) because if lifetime of reference to Foo (&mut self) and of reference to &mut i32 (self.x) are not equals an invalid memory status could happen.

To help make sense of what may happen with inner reference mutability and lifetime narrowing consider this invalid rust snippet:

let mut my_ref: &mut i32 = &mut 1;
let mut f = Foo { x: my_ref }; 
{                                 | <--- narrowed lifetime scope
    let y = &mut 5;               |
    f.x = y;                      |
}                                 | <---
// ERROR: invoking f.x() when self.x is no more valid!
f.x();

See this answer for more details.

attdona
  • 17,196
  • 7
  • 49
  • 60
  • Thanks for the answer. "If the inner reference is immutable the compiler is assured that no memory issues can arise for narrowing the lifetime scope.". What do you mean by "narrowing the lifetime scope"? Shouldn't self.x has the lifetime of the f instance? Which variable's lifetime scope is narrowed? – Helin Wang Aug 10 '19 at 05:57
  • `self.x` (intending the lifetime of reference self.x is pointing) and a reference to a `Foo` as the &self argument of `Foo::x(&mut self)` may have different lifetimes. See my update for the meaning about narrowing the lifetime scope, I hope it may be more clearer. – attdona Aug 10 '19 at 14:52