An Inopportune Consumption

30 May 2018

I just failed to implement what looked to be a relatively simple opportunistic replacement so that the compiler would accept the mutated code. But I’m getting ahead of myself.

The idea was to replace while let Some(foo) = bar { .. } and if let Some(foo) = bar { .. } by putting the bar in a function. That function would look like the following:

pub fn<L: Letter> l(l: L, count: usize, flag: &AtomicUsize, mask: usize) -> L;

Letter would default to returning self , and be specialized for Option s, among other types. But how can this go wrong?

Attack of the borrowck

Let’s for a moment ignore the specialization issue and just replace l with a simple id function: pub fn id<T>(t: T) -> T { t } . Then we’d be mutating

while let Some ( ref mut x ) = y { if foo ( x ) { break ; } }

to

while let Some ( ref mut x ) = id ( y ) { // ← note the `id` here! if foo ( x ) { break ; } }

But what if y is non- Copy ? In that case, our id call consumes y ! No wonder this doesn’t compile. So what to do? Obviously we should be able to borrow, but now we must take care to correctly mutate the level of borrowing:

If we take nothing out of the let binding (i.e. no non-wildcard matches), or only take out ref s, we should be fine with introducing a & reference, e.g. while let &Some(ref foo) = &next() { .. } . If however, like in the above example we take any ref mut , we’d need to mutate it to something akin to while let &mut Some(ref foo) = &mut next() { .. } . Finally, if we take anything by value, we’re dealing with a Copy , so we mut reference &mut ably regardless in order not to mutate an accidental copy instead of the original value.

Unfortunately, there is one roadblock because of RFC #2005 (match ergonomics): On current nightly, we can no longer infer the required binding mode from the pattern, and we cannot know the type of our destructured value in general (for example we may not know what foo.next() returns).

Note that following the RFC will only automate putting the required ref or ref mut there, not change the type of the destructured expression. Also we cannot in general use a mutable reference, because that, too, may fall afoul of the borrow checker (please someone budget that for me, my mutator is dying).

Trying to make the code more complex to move consuming the value somewhere else is not going to work in the general case, but perhaps someone reading this has an idea how to solve this?