fn find_or_default_mut<'a, T, P>(
xs : &'a mut Vec<T>,
mut pred : P,
default : T,
) -> &'a mut T
where P: FnMut(&T) -> bool
{
let res = xs.iter_mut().find(|x| pred(&**x));
if let Some(this_x) = res {
this_x
} else {
xs.push(default);
xs.last_mut().expect("just pushed")
}
}
which should be safe, as the borrow of xs via res finishes before we start using xs again in the else branch. But this does not pass the borrow checker.
It was my understanding that non-lexical lifetimes was supposed to solve situations such as these. Why isn't it doing so here? Is it just an incompleteness in the borrow checker, or have I overlooked something?
this is a polonius issue , and will be fixed when the polonius extension of the borrow-checker is stabilized.
the reason is because 'a is an external lifetime, so when it calls iter_mut() it calls it with the 'a lifetime, which ends after the function ends so from the compiler's persepective it is borrowed until 'a in all branches
In general, the way to avoid this problem, until we get a better borrow checker, is:
If a mutable borrow is going to be returned, then do not take it until you are sure you will return it.
Unfortunately, that’s not possible to arrange very directly in this case, because iter_mut() is needed for the condition. You can, however, fall back to explicit indexing.
This way, the only data that flows from the conditional part to the borrow-returning part is i: usize, which has no lifetime and so doesn’t cause trouble.
Unrelatedly, in the next release of Rust, Vec::push_mut() will become stable and you will no longer need the .last_mut().expect() to get a mutable reference to what you just pushed.
And, as a bonus, position on slice iterators makes sure to tell LLVM that the position it returns is in-range of the slice, so it won't even generate a bounds check for that indexing (in optimized builds).