I think it's not about the order between temp2
and temp3
.
Two-phase borrow is for the mutable (unique) reference to be temporarily shared (reservation point), and another shared reference coexist, then the mutable reference restores as mutable in the function call (activation point). It means the desugaring in the issue is incorrect. The desugar for two-phase is like
// not specific to OP or the linked issue, just stole from the documentation
let mut v = Vec::new();
let temp1 = &two_phase v; // reservation point: mutable reference is first
let temp2 = v.len(); // shared reference is second
Vec::push(temp1 /* activation point */, temp2);
For OP, I'll minimize the problem as this: Rust Playground
fn main() {
let mut a = A { b: B { c: 123 } };
a.mutate(a.share());
}
struct A {
b: B,
}
impl std::ops::Deref for A {
type Target = B;
fn deref(&self) -> &B {
&self.b
}
}
impl std::ops::DerefMut for A {
fn deref_mut(&mut self) -> &mut B {
&mut self.b
}
}
impl A {
// uncomment this to see error:
// cannot borrow `a` as immutable because it is also borrowed as mutable
// fn mutate(&mut self, _: ()) {}
}
struct B {
c: u8,
}
impl B {
fn mutate(&mut self, _: ()) {}
fn share(&self) {}
}
And the MIR related:
// success
bb0: {
_2 = B { c: const 123_u8 };
_1 = A { b: move _2 };
_4 = &mut _1; // phase1: reservation point (treated as shared borrow)
_8 = &_1;
_7 = <A as Deref>::deref(move _8) -> [return: bb1, unwind: bb4];
}
bb1: {
_6 = &(*_7); // shared borrow
_5 = B::share(move _6) -> [return: bb2, unwind: bb4]; // return a value that doesn't conflict with _4
}
bb2: {
// Each two-phase borrow is assigned to a temporary that is only used once.
_3 = A::mutate(move _4 /* phase2: activation point (treated as unique borrow) */, move _5) -> [return: bb3, unwind: bb4];
}
// failure
bb0: {
_2 = B { c: const 123_u8 };
_1 = A { b: move _2 };
_6 = &mut _1; // reservation point???
_5 = <A as DerefMut>::deref_mut(move _6 /* activation point??? */) -> [return: bb1, unwind: bb5];
// the temporary is used once already (the move consumes the temporary)
}
bb1: {
_4 = &mut (*_5); // reborrow (treated as unique borrow): no longer being a two-phase borrow, otherwise code would pass
_10 = &_1; // shared borrow: conflicts with _4 [error]
_9 = <A as Deref>::deref(move _10) -> [return: bb2, unwind: bb5];
}
bb2: {
_8 = &(*_9);
_7 = B::share(move _8) -> [return: bb3, unwind: bb5];
}
bb3: {
_3 = B::mutate(move _4, move _7) -> [return: bb4, unwind: bb5];
}
Each two-phase borrow is assigned to a temporary that is only used once. As such we can define:
https://rustc-dev-guide.rust-lang.org/borrow_check/two_phase_borrows.html