A common pattern in languages with nulls is
if (x != nullptr) {
return expr;
}
x->blah(); // Treat x as nonnull, no safety though!
This is really, really useful for readability, because it avoids rightward drift (which if let Some(_)
tends to make really really bad in long chains of if-lets).
What I want is the following behavior:
if let None = x {
return expr
}
let Some(x) = x;
In particular, if an if-let
can only execute if x
matches a pattern, and its block types as !
, then that pattern should be “ruled out” from the list of patterns that are needed to make a match irrefutable. The same holds for breakless while-lets, and match arms that type as !
. For example:
enum E { A, B, C, D }
let x: E = E::A;
// E::A is ruled out, since the type is `!`.
if let A = x { return }
// No E::A arm is ok, since x match E::A has been ruled
// out.
match x {
E::B => panic!(), // Types as `!`, so its pattern is
// ruled out in the containing block.
E::C => {},
E::D => {},
}
while let E::C = x {
// no breaks
}
// To reach this line, x cannot be E::C, so that
// pattern is ruled out.
let E::D = x; // Irrefutable, since other variants have
// been ruled out.
(For silly bonus points, you could type x as !
if all its variants are ruled out, but this seems kind of like a bad idea…)
I think this mostly only targets Option
right now; if and when we get negative if let, you might imagine that the following is allowed:
// Imaginary syntax, do not bikeshed.
if !let Some(..) = x {
return
}
// Here, *all* patterns except Some are ruled out.
let Some(x) = x;
Of course, you might say “why not just use Option<T>: Try
?” Like, you could, but that doesn’t give you a chance do to processing between the is-None check and the return, and you are limited to returning an impl Try
. You could also use Option
combinators, but I’ve found that long chains of combinators and closures are not exactly readable (see: why we have for-in
, and why Haskell has do
). Anything more complicated than x.map(..).unwrap_or()
is almost certainly too clever.