The following code involves a very subtle bit of borrow checker dodging. The code itself describes the reasoning. The questions:
- Is this actually safe?
- Is this the recommended way to express the unsafe operations performed? Should I use pointers instead?
- Will the new Polonius borrow checker be able to reason about patterns like this?
/// Insert a new data element at a given key.
pub fn insert<'a, K: Eq, V>(this: &'a mut Vec<(K, V)>, key: K, val: V) -> &'a mut V {
// Safety: As indicated below, we would like to return val1 directly in the loop,
// but rust will reject this, claiming a double borrow, and we instead use some
// unsafe hacks to circumvent the borrow checker. To show this is safe, consider
// two cases.
// - If the return is exercised (we found an element and early out):
// - let 'b be 'a (the borrow of self),
// - and let 'c be empty
// - Otherwise (we did not find an element, exit the loop and terminate normally):
// - let 'b be the duration of the loop,
// - and let 'c be from the end of the loop until the end of 'a
// In either case, 'b and 'c are disjoint, so the "double borrow" is safe.
// The borrow checker reasons that 'b has to be at least 'a because it is returned,
// and therefore it overlaps with 'c, but these happen in mutually exclusive
// situations.
for (key1, val1) in & /* 'b */ mut *this {
if key == *key1 {
// return val1; // we would like to write this
return unsafe { // safety, see above. We know we are in the first case, so 'b = 'a
std::mem::transmute::<&/* 'b */ mut V, &/* 'a */ mut V>(val1)
}
}
}
let this = & /* 'c */ mut *this;
this.push((key, val));
&mut this.last_mut().unwrap().1
}
This is what I'd prefer to write:
/// Insert a new data element at a given key.
pub fn insert<'a, K: Eq, V>(this: &'a mut Vec<(K, V)>, key: K, val: V) -> &'a mut V {
for (key1, val1) in &mut *this {
if key == *key1 {
return val1;
}
}
let this = &mut *this;
this.push((key, val));
&mut this.last_mut().unwrap().1
}
but it fails:
error[E0499]: cannot borrow `*this` as mutable more than once at a time
--> src/lib.rs:8:16
|
2 | pub fn insert<'a, K: Eq, V>(this: &'a mut Vec<(K, V)>, key: K, val: V) -> &'a mut V {
| -- lifetime `'a` defined here
3 | for (key1, val1) in &mut *this {
| ---------- first mutable borrow occurs here
4 | if key == *key1 {
5 | return val1;
| ---- returning this value requires that `*this` is borrowed for `'a`
...
8 | let this = &mut *this;
| ^^^^^^^^^^ second mutable borrow occurs here