enum E {
A,
B,
C,
D,
E,
}
fn process_e(e: E) {
match e {
E::A | E::B => {
match e {
E::A => todo!(),
E::B => todo!(),
}
}
_ => todo!(),
}
}
raises:
error[E0004]: non-exhaustive patterns: `E::C`, `E::D` and `E::E` not covered
--> src/main.rs:12:19
|
12 | match e {
| ^ patterns `E::C`, `E::D` and `E::E` not covered
|
note: `E` defined here
--> src/main.rs:4:5
|
1 | enum E {
| -
...
4 | C,
| ^ not covered
5 | D,
| ^ not covered
6 | E,
| ^ not covered
= note: the matched value is of type `E`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
|
14 ~ E::B => todo!(),
15 ~ E::C | E::D | E::E => todo!(),
|
This is indeed possible to solve at a type system level; it's called flow-sensitive typing. TypeScript and Kotlin both provide forms of flow-sensitive typing.
Technically, flow-sensitive typing isn't necessary; if you rewrite the example slightly to
match e {
e2 @ (E::A | E::B) => {
match e2 {
E::A => todo!(),
E::B => todo!(),
}
}
_ => todo!(),
}
then refinement typing is sufficient to determine the inner match is exhaustive.
There currently exists a one-person experiment to introduce pattern refined types into Rustc as the implementation of the internal #[rustc_scalar_valid_start] attributes underlying the NonZeroUnn types, but any introduction of surface-level syntax for such is a ways off yet.
If someone wanted to introduce some small amount of flow-sensitive typing to Rust, then after first getting sign-off from the types team the likely first step would probably be attaching the requisite information to replace the => todo!() addition with => unreachable!() if-and-only-if flow-sensitive typing indicates that the arm is unreachable semantically (but not yet statically).
For the specific case of Result-like enums, it's already the case that #![feature(never_type)] makes it so that Result<T, !> actually essentially deletes the Err variant, allowing you to treat Ok(_) as an irrefutable pattern (e.g. let Ok(val) = res;).
Perhaps this could be done by having inheritance-like relationships between enums?
pub enum AB {
A, B
}
pub enum ABCD {
#[flatten] enum AB, // makes ABCD::A and ABCD::B
C, D
}
match e {
val @ enum AB => match val { AB::A | AB::B => {} }
ABCD::C | ABCD::D => {}
}
When working with images I often need different subsets of color spaces (RGB/Gray/YUV/CMYK with and without alpha, float, planar layout, premultiplied, etc.) so being able to easily assemble color enums from subsets would be nice.
It could if the discriminant values don't conflict. For enums from the same crate the compiler should be able to compute non-overlapping discriminants when they're not explicitly specified.