A weird thing about while let with ref

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 for ref mut n.
  • Blue parts change to ((*_3).1) when ref 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 of bb6, 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...

1 Like