In mutagen, I'm injecting various
mutations in the code. One thing I'd like to mutate is the pattern
if let Ok(x) = y { .. }
. However, this poses quite the challenge, as I
cannot know the type of y
– the user could have built their own enum with an
unary Ok
variant. I can still opportunistically mutate it for cases where we
actually have a Result
whose error type implements Default
using a trait
that looks like the following simplified:
#![feature(specialization)]
pub trait Errorer {
fn err(self, mutate: bool) -> Self;
}
impl<X> Errorer for X {
default fn err(self, _mutate: bool) -> Self {
self
}
}
impl<T, E> Errorer for Result<T, E>
where
E: Default,
{
fn err(self, mutate: bool) -> Self {
if mutate {
Err(Default::default())
} else {
self
}
}
}
Alas, there aren't that many errors which implement Default
, so this is
not too useful. Even an implementation for Result<T, Box<Error>>
would give
us more bang for the buck (and be completely possible). However, given that I
don't care much about code actually inspecting the error, I wonder if I could
do a general implementation by extending the mutation of the above code to
match Errorer::err(y, mutation) {
Ok(x) => { .. }
Err(x) => { mem::forget(x); }
}
and have err
return Err(mem::uninitialized())
when mutating – so is this
behavior safe? Note: I'm returning Err(mem::uninitialized())
from a method,
only to mem::forget
it later. I see no way this could panic, so we should
assume that the value will be indeed forgotten.
Is this defined behavior or should I expect nasal demons?
Option<Box<T>>
the same size asBox<T>
). I'm not aware of any layout optimisations that affectResult
, but I also see no reason they couldn't be added in future. If that were the case,mem::uninitialized
could leave theE
value in an invalid state that changes how theResult
is interpreted. – HelbonaResult<T, !>
could and (someday) should have the same layout asT
. – Lofty