I think this is best explained with this code upfront:
struct Borrowed<'a>(&'a Buf);
struct Buf;
impl Buf {
fn adapt(&mut self) {}
}
fn borrow(buf: &mut Buf) -> Option<Borrowed> {
buf.adapt(); // think Vec::resize(), so we do need a mutable borrow initially
// When done, we keep a shared borrow, but borrowchk doesn't know about that apparently
Some(Borrowed(buf))
}
fn locate<'a>(buf: &'a mut Buf) -> Option<Borrowed<'a>> {
for _c in 1..3 {
// This works!
// if _c % 2 == 0 {
// return borrow(buf);
// }
// This also works but is wasteful
// if borrow(buf).is_some() {
// return borrow(buf);
// }
if let Some(b) = borrow(buf) {
return Some(b)
}
}
None
}
fn main() {
locate(&mut Buf);
}
Here is the playground link for the code above, which doesn't borrowcheck:
error[E0499]: cannot borrow `*buf` as mutable more than once at a time
--> src/main.rs:24:33
|
14 | fn locate<'a>(buf: &'a mut Buf) -> Option<Borrowed<'a>> {
| -- lifetime `'a` defined here
...
24 | if let Some(b) = borrow(buf) {
| ^^^ mutable borrow starts here in previous iteration of loop
25 | return Some(b)
| ------- returning this value requires that `*buf` is borrowed for `'a`
I have found a similar problem referenced in a GitHub issue (but can't find it now) which indicates this is a known limitation of NLL for performance reasons.
What is a workaround for this that isn't wasteful and maintains a buffer that is passed in?
Due to this, right now I have to make an expensive pack lookup twice as a workaround.