5
votes

Let us consider a simple enum implementation with a static method that check whether a value has an associated value (the efficiency of the implementation is not to be regarded here):

enum Letter {
    Alpha = -1,
    A = 0,
    B = 1,
    C = 2,
}

impl Letter {
    pub fn in_enum(value: isize) -> bool
    {
        match value {
            -1 => true,
            0 => true,
            1 => true,
            2 => true,
            _ => false,
        }
    }
}

Now, let us write a macro for building enums with an equivalent in_enum method. The macro below was written with some guidance from the Serde guide for enum deserialization as numbers, in which matching for enum variant values also occurs.

macro_rules! my_enum {
    ($name:ident { $($variant:ident = $value:expr, )* }) => {
        #[derive(Clone, Copy, Debug, Eq, PartialEq)]
        pub enum $name {
            $($variant = $value,)*
        }

        impl $name {
            pub fn in_enum(value: isize) -> bool
            {
                match value {
                    $( $value => true, )*
                    _ => false,
                }
            }
        }
    }
}

my_enum!(Letter {
  Alpha = -1,
  A = 0,
  B = 1,
  C = 2,
});

Playground. Now, the compiler won't accept the variant with a negative integer.

error: expected pattern, found `-1`
  --> src/main.rs:13:24
   |
13 |                     $( $value => true, )*
   |                        ^^^^^^

This seems to happen regardless of how I write this pattern down in the macro, or whether I use i32 or isize for the value method parameter. Changing the fragment specifier of $value to pat is also out of the question: the compiler will refuse to build the enum, even without negative variant values.

error: expected expression, found `-1`
 --> src/main.rs:5:26
  |
5 |             $($variant = $value,)*
  |                          ^^^^^^

What's surprising about this is that it works without using macros, as well as when I discard the Alpha variant.

Why does this happen?

1
Because you matched an expr, not a pat. Expressions and patterns are two totally different parts of the language. - DK.
@DK Indeed they are. But that alone doesn't explain why 0 is accepted as a pattern while -1 isn't. The part where -1 is composed of the - token followed by the literal could be the culprit here. - E_net4 the voter
Huh. I'm surprised that it does accept 0. I suspect it's because -1 is (if I remember correctly) internally represented as Neg::neg(1) rather than a literal negative integer, and that might be tripping up whatever part of the code is trying to convert the expr AST into a pattern. - DK.

1 Answers

5
votes

This is a bug in the compiler and is already fixed in the nightly version as of today (Jul 5, 2017).