Question about lifetime of &self and &mut self

I am writing some codes with rust. I wonder how compiler check the lifetime of &self and &mut self.

Here is my codes.

struct P {
  val: Vec<usize>,
}

impl P {
  fn get(&self, i: usize) -> &usize {
    &self.val[i]
  }
}

struct A<'a> {
  val: &'a P,
}

struct B<'b> {
  a: Vec<A<'b>>,
}

impl<'b> B<'b> {

  fn get_ref(&mut self) -> &usize {
    self.a.last_mut().unwrap().val.get(0)
  }

  fn t1(&mut self) {
    let x =  self.a.last().unwrap().val.get(0);
    self.t2(x);
  }

  fn t2(&mut self, x: &usize) {
  }
}

I am little confused about the lifetime of x in B::t1.

Firstly, the implementation of t1 can be compiled.

However if we replace with get_ref. That is,

  fn t1(&mut self) {
    let x =  self.get_ref();
    self.t2(x);
  }

Compiler has an error.

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/main.rs:34:5
   |
27 |     let x = self.get_ref();
   |             ---- first mutable borrow occurs here
...
34 |     self.t2(x);
   |     ^^^^    - first borrow later used here
   |     |
   |     second mutable borrow occurs here

What is the differences between two implementations here?

1 Like

This is unnecessarily tying the input and output lifetimes together. If you define it like this instead, your alternate implementation compiles:

  fn get_ref(&mut self) -> &'b usize {
    self.a.last_mut().unwrap().val.get(0)
  }
1 Like

Why this is ok? What does 'b tell us? I am confused.

When you write this:

fn get_ref(&mut self) -> &usize

That is short-hand for this:

fn get_ref<'x>(&'x mut self) -> &'x usize

That is to say, the function takes a &'x mut B<'b> as argument and returns an &'x usize. To create a mutable reference to the B<'b>, you must mutably borrow B<'b> for some duration of time, and the lifetime 'x is that duration. This means that get_ref returns an &usize that is valid for the duration in which you borrowed the B<'b>.

Now the problem is, when you call self.tx2(x), that also requires mutably borrowing the B<'b>. Since you're not done using x yet, the first borrow of B<'b> must still be active. But this means that you now have two mutable borrows at the same time; that's an error.

When you use &'b usize instead, the above logic does not apply. The &usize reference can be valid for longer than the duration in which get_ref borrows B<'b>, so the borrow of B<'b> can end before the t2 call.

7 Likes

Understand. Thanks.