Borrow checker again - Mutable / Immutable borrow

Hi

I'm currently fighting with the borrow checker again. It is basically telling me that I'm borrowing a mutable reference, and later on an immutable reference. The funny thing is, that the mutable borrow is enclosed in its own scope, so it should be out of scope before the immutable borrow occurs.

I created two little playgrounds, where the first one is not working (as described above). During the process of trying to find out what might be wrong, I created a slightly different playground that does work (but obviously misses some code that seems to be responsible for the error). I still can't really explain why the borrow checker is not happy with the first playground though, so I hope that somebody might have an idea...

First (failing) example
Second (working) example

You have a DatabaseClient<'a> with a method that takes &'a mut self -- that borrows it for the rest of its life. You can just use &mut self for a more localized borrow.

Oh, I see. I might have understood something wrong then. I was under the impression that lifetimes specified that a certain borrow cannot outlive a struct. But that doesn't mean that I would tie that borrow to the lifetime of the struct...

A Struct<'a> means that it has borrowed something for the lifetime 'a. That means the struct itself cannot outlive 'a, since its borrow isn't valid any longer than that. The struct could be held up to the maximum extent of that lifetime, or dropped sooner at the whim of whoever owns it -- as long as the struct isn't also borrowed itself.

&'a T and &'a mut T actually work the exact same way, as types that have a borrow (of a T) that can last up to the lifetime 'a. The references could be discarded sooner, but cannot be used later. A &'a mut T further indicates that it has an exclusive borrow for that lifetime.

So when you pass &'a mut self to a function, think of that as a black box. You're saying to the callee, "here's a reference that you can keep up to the lifetime 'a." Whether they actually do anything with that is not part of the borrow checker, but they could stash it somewhere for the entire 'a, so it's out of the caller's hands until then.

When you use an anonymous lifetime, just &mut self, then the callee can only assume that it will be valid for the duration of the call. If the return type is tied to that same lifetime, then the caller can keep that return value longer, limited by the lifetime they had available on the input in the first place. But this is where it can be more flexible, explictly ended by a scope, or implicitly ended after last use by non-lexical lifetimes (NLL).

Hope that helps!

3 Likes

Wow, that was a great explanation, that helped a lot! Many thanks for your effort!