Consider this code:
enum MyError {
BadError, NotSoBad,
}
#[inline(never)]
fn do_stuff1<T>(m: Result<T, MyError>) -> bool {
m.is_err()
}
#[inline(never)]
fn do_stuff2<T>(m: Result<T, MyError>) -> bool {
if let Err(MyError::BadError) = m {
true
} else {
false
}
}
The resulting assembly (see playground):
playground::do_stuff1:
cmpb $1, %dil
sete %al
retq
playground::do_stuff2:
testb %dil, %dil
setne %cl
testb %sil, %sil
sete %al
andb %cl, %al
retq
Testing result.is_err()
results in just one byte comparison, which seems reasonable but pattern-matching for the specific error results in a few more instructions.
This is because the error type is actually encoded as a separate byte from the error flag. A Result<u64, MyError>
is laid out like this:
.--- is error?
/
/ .--- error variant
/ /
01 01 XX XX XX XX XX XX 00 00 00 00 00 00 00 01
\ / \ /
`-- padding --' `---- data (u64) ---'
My expectation was that a single byte would be used to encode both the fact that there is an error and which variant the error is, which would allow for simpler assembly.
Is there a reason why this optimisation isn't possible, or are there some tradeoffs that would make it undesirable?