An Inopportune Consumption
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?