Okay, so I replaced the innards of the loop with a trivial expression 42;
. This allowed me to compare the MIRs. Unfortunately it still does not make sense to me.
The MIR shown in the following graph is the MIR for when &mut temp.next
is used.
- Red parts are only present for
&mut temp.next
, not forref mut n
. - Blue parts change to
((*_3).1)
whenref mut n
is used.
// rust code
while let Some(n) = &mut temp.next {
42;
}
Now, we have to imagine replacing the orange line with the MIR of temp = n.next.as_mut().unwrap()
. This gets annoying and complicated to show because each of the function calls breaks it up into a new basic block in order to introduce unwind edges. If I try to simplify that, the MIR for the loop that compiles looks something like:
MIR for loop that compiles (no image)
// rust code
while let Some(ref mut n) = temp.next {
temp = n.next.as_mut().unwrap();
}
bb5: {
_7 = discriminant(((*_3).1));
switchInt(move _7) -> [1isize: bb6, otherwise: bb9];
}
bb6: {
StorageLive(_8);
_8 = &mut ((((*_3).1) as Some).0);
StorageLive(_9);
StorageLive(_10);
StorageLive(_11);
_11 = &mut ((*(*_8)).1);
_10 = const Option::<T>::as_mut(move _11);
StorageDead(_11);
_9 = const Option::<T>::unwrap(move _10);
StorageDead(_10);
_3 = &mut (*(*_9));
StorageDead(_9);
StorageDead(_8);
goto -> bb5;
}
bb9: {
StorageDead(_8);
StorageDead(_3);
drop(_2) -> bb10;
}
bb10: {
return;
}
Putting it all together I get:
where red and blue have the same meaning as before, and the green line would appear to be the important one.
If this is correct, then to address @RustyYato :
-
Yes,
&mut temp.node
results in a borrow with a slightly wider scope... -
but... this borrow still ends before every iteration.
StorageDead(_6)
appears at the end ofbb6
, before it enters the next iteration, so I don't understand why there is an issue.
The primary difference seems to be that, in the loop that compiles, n
(_8
) borrows directly from temp
(_3
), but in the loop that fails to compile, it only borrows indirectly through _6
...