user1244932
user1244932

Reputation: 8092

match inside macro_rules - can not use '_'

I am trying to define macro for simplification of creation of enum that is possible convert to/from str:

macro_rules! define_enum_with_str_values {
    ($Name:ident { $($Variant:ident => $StrVar:expr),* $(,)* }) => {
        #[derive(Debug, Clone, Copy, PartialEq)]
        pub enum $Name {
            $($Variant),*,
        }
        impl Into<&'static str> for $Name {
            fn into(self) -> &'static str {
                match self {
                    $($Name::$Variant => $StrVar),*
                }
            }
        }
        impl FromStr for $Name {
            type Err = BaseError;
            fn from_str(s: &str) -> Result<Self, Self::Err> {
                let obj = match s {
                    $($StrVar => $Name::$Variant),*
                };
                Ok(obj)
            }
        }
    }
}

define_enum_with_str_values!(Foo { Aa => "a", Bb => "b" });

This code doesn't compiled, because of I did not define '_' rule, but if I define '_' rule:

    impl FromStr for $Name {
        type Err = BaseError;
        fn from_str(s: &str) -> Result<Self, Self::Err> {
            let obj = match s {
                $($StrVar => $Name::$Variant),*
                    _ => {}
            };
            Ok(obj)
        }
    }

I got such compile time error:

error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, `}`, or an operator, found `_`
  --> foo.rs:74:25
   |
73 |                     $($StrVar => $Name::$Variant),*
   |                                                 - expected one of 8 possible tokens here
74 |                         _ => {}
   |                         ^ unexpected token
...
82 | define_enum_with_str_values!(Foo { Aa => "a", Bb => "b" });
   | ----------------------------------------------------------- in this macro invocation

Upvotes: 0

Views: 329

Answers (1)

DK.
DK.

Reputation: 59015

Consider what happens when you expand that macro. The match in question will look like:

let obj = match s {
    "a" => Foo::Aa , "b" => Foo::Bb
        _ => {}
};

Note the lack of a comma between the "b" and _ arms. The simplest fix is to ensure there's always a comma after each arm:

let obj = match s {
    $($StrVar => $Name::$Variant,)*
    _ => return Err(BaseError)
};

Upvotes: 2

Related Questions