reinis_mx
reinis_mx

Reputation: 1316

Why is this borrow considered to be "used later" even though it clearly isn't?

Just when I thought I had lifetimes figured out...

The compiler won't let this happen:

fn main() {
    let mut thing = Thing();

    let mut a = MutReference { data: &mut thing };
    a.another_reference_mut();
    a.another_reference_mut();
}

struct Thing();

struct MutReference<'a> {
    data: &'a mut Thing,
}

impl<'a> MutReference<'a> {
    fn another_reference_mut(&'a mut self) -> MutReference<'a> {
        MutReference { data: self.data }
    }
}

The error message is rather confusing:

error[E0499]: cannot borrow `a` as mutable more than once at a time
 --> src/main.rs:6:5
  |
5 |     a.another_reference_mut();
  |     ------------------------- first mutable borrow occurs here
6 |     a.another_reference_mut();
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^
  |     |
  |     second mutable borrow occurs here
  |     first borrow later used here

What does he mean by "first borrow later used here"? Clearly the first borrow is not used there – it's dropped on line 5 and never used again. Even wrapping that line in {} does not help.

After some tinkering, I found out that creating a separate lifetime for the function fixes the problem:

impl<'a> MutReference<'a> {
    fn another_reference_mut<'b>(&'b mut self) -> MutReference<'b> {
        MutReference { data: self.data }
    }
}

('b may also be elided here)

Now I'm pretty sure that this is the correct solution, but I'm still not sure why the old code wasn't working. It seems I had written in some lifetime constraint I did not intend, but I don't know how to properly reason about the lifetimes in this situation, and the compiler error doesn't help.

My understanding was that, when the same lifetime is used on several "input" references and an "output" reference, all it means is that the "output" reference cannot outlive any of the input references... but it seems something else is going on here – it's as if the &'a mut self input reference was "forcing" the return value to live longer than it normally would? How does this work? What was wrong with my old code?

Upvotes: 4

Views: 1256

Answers (1)

jthulhu
jthulhu

Reputation: 8688

The reason is quite simple if you try to run through you code, labeling each lifetime explicitly. What actually happens is that, in the first code, the another_reference_mut function is not generic over a lifetime: the lifetime of the &mut self is takes is 'a, which is the same as the one used by &'a mut Thing inside MutReference<'a>. This means that the first time you call a.another_reference_mut, you have to take a mutable borrow that lives as long as &mut Thing. When you try to call that a second time, you have to make an other mutable borrow of a, which means the first mutable borrow must have a lifetime which has "ended" (roughly speaking), but which implies that &mut Thing is no longer valid, meaning a itself is no longer valid. Which is why Rust is unable to find lifetimes that match your requirements.

In your second version, you make the mutable borrow independent (or, at least, not forcly equal to) from the lifetime of &mut Thing, and therefore of a. Rust can then make 'b shorter than 'a, which allows you to borrow twice mutable a.

Upvotes: 4

Related Questions