Your concrete syntax suggestion for case 2 is no good because match input as Enum { ... }
already means something -- it's the same as match (input as Enum) { ... }
.
Also, I think limiting case 2 to match
expressions and values of the enum type itself is too restrictive. Here's some actual code of mine that could benefit from case 2 support:
#[non_exhaustive]
pub enum ChecksumAlg { Blake3, SHA256, }
struct ParseChecksumAlgError;
impl FromStr for ChecksumAlg {
type Err = ParseChecksumAlgError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_char_boundary(5) {
let (prefix, suffix) = s.split_at(5);
if prefix.eq_ignore_ascii_case("blake")
&& (suffix == "3" || suffix == "-3")
{ return Ok(Self::Blake3); }
}
if s.is_char_boundary(3) {
let (prefix, suffix) = s.split_at(3);
if prefix.eq_ignore_ascii_case("sha")
&& (suffix == "256" || suffix == "-256")
{ return Ok(Self::SHA256); }
}
Err(ParseChecksumAlgError)
}
}
There might be a better way to write this but that's not important right now; the point is that a series of "if this, return that" checks with fallthrough on failure is a totally natural way to write a parser. Also that what you're returning might be a derived value, e.g. Result<Enum, Failure>
and the error case(s) should not interfere with the check.
I'm thinking that a better way to express case 2 would be with an attribute, applicable to both functions and single expressions:
impl FromStr for ChecksumAlg {
type Err = ParseChecksumAlgError;
#[exhaustive_value]
fn from_str(s: &str) -> Result<Self, Self::Err> {
...
}
}
With no arguments, #[exhaustive_value]
requires there to be at least one live control flow path from entry to exit of the function (or expression) that produces each possible variant of the function's (expression's) return type, recursively. In this case, Ok(Self::Blake3)
, Ok(Self::SHA256)
, and Err(ParseChecksumAlgError)
must all be possible returns.
#[exhaustive_value]
takes optional keyword arguments, there currently being only one, exclude = <pattern>
. Variants that match the pattern are required not to be returnable. For instance
impl FromStr for ChecksumAlg {
type Err = ParseChecksumAlgError;
#[exhaustive_value(exclude = Err(_))]
fn from_str(s: &str) -> Result<Self, Self::Err> {
...
}
}
the body of from_str
must have live control flow paths that return all variants of Ok(Self)
but must not return any Err()
values. (Once type Err = !
works in this context, #[exhaustive_value]
should automatically exclude variants that are or contain the never type.)
("Live control flow path" needs to be given a precise definition that does not depend on optimization level.)