strum icon indicating copy to clipboard operation
strum copied to clipboard

EnumString macro ignores parse_err_ty when there is a default variant

Open scovich opened this issue 7 months ago • 1 comments

Related to https://github.com/Peternator7/strum/issues/307

In theory, this new feature of EnumString should allow infallible parsing of an enum with a default variant:

The default error type is strum::ParseError. This can be overridden by applying both the parse_err_ty and parse_err_fn attributes at the type level. parse_error_fn should be a function that accepts an &str and returns the type parse_error_ty.

(aside: there are some stray references to parse_error_fn and parse_error_ty)

Unfortunately, https://github.com/Peternator7/strum/pull/380/ that added the feature explicitly sets the error type back to strum::ParseError if the enum has a default variant, see here.

Thus, the following:

#[derive(EnumString)]
#[strum(
    parse_err_fn = not_needed_because_parsing_is_infallible,
    parse_err_ty = std::convert::Infallible,
)]
pub enum InfallibleEnum {
    Foo,
    Bar,
    #[strum(default)]
    Unknown(String),
}

fn parse_infallible(s: &str) -> Result<InfallibleEnum, std::convert::Infallible> {
    s.parse()
}

fails to compile with:

error[E0271]: type mismatch resolving `<InfallibleEnum as FromStr>::Err == Infallible`
  --> test.rs:24:7
   |
24 |     s.parse()
   |       ^^^^^ expected `ParseError`, found `Infallible`

For more information about this error, try `rustc --explain E0271`.

Perhaps this was just an oversight in the original PR? Or is there a specific (but undocumented) reason it's important for enums with default variants to ignore the custom error type specification?

scovich avatar Apr 08 '25 16:04 scovich

The expanded macro code confirms the custom error type was ignored:

    #[allow(clippy::use_self)]
    impl ::core::str::FromStr for InfallibleEnum {
        type Err = ::strum::ParseError;
        #[inline]
        fn from_str(
            s: &str,
        ) -> ::core::result::Result<
            InfallibleEnum,
            <Self as ::core::str::FromStr>::Err,
        > {
            ::core::result::Result::Ok(
                match s {
                    "Foo" => InfallibleEnum::Foo,
                    "Bar" => InfallibleEnum::Bar,
                    _ => {
                        return ::core::result::Result::Ok(
                            InfallibleEnum::Unknown(s.into()),
                        );
                    }
                },
            )
        }
    }

scovich avatar Apr 08 '25 16:04 scovich