[Solved] Using value of enum inside macros both in pattern and as expression


#1

I’m trying to write a macro generating test cases. Here’s the code I’ve got working:

#[derive(Debug, PartialEq)]
enum E {V1, V2}

fn generate(id: i32) -> Option<E> {
    match id {
        1 => Some(E::V1),
        2 => Some(E::V2),
        _ => None
    }
}

macro_rules! test_enum {
    ($name:ident, $input:expr, $x:expr) => {
        #[test]
        fn $name() {
            match generate($input) {
                Some(ref x) if *x == $x.unwrap() => {},
                x @ Some(_) => panic!("Expected {:?}, got {:?}", $x, x),
                None => panic!("Expected {:?}, got None", $x)
            }
        }
    }
}

test_enum!(test1, 1, Some(E::V1));
test_enum!(test2, 2, Some(E::V2));

I’d like to remove explicit match guard and replace it with the pattern matching. However, just replacing the pattern with $x doesn’t work, because expressions can’t be used as patterns; and if I change the type of $x to pat, it can’t be used as expression (inside panic!). I don’t want to pass two identical token sequences just to satisfy the compiler. Is there any way?

Playground: https://play.rust-lang.org/?gist=000c67bef89c54b62a42798a3f6f81a4&version=stable&mode=debug&edition=2015


#2

Can’t you use stringify! to get a printable represantation of your pattern? Even today, that had the big benefit, that it would also work when your input doesn’t implement std::fmt::Debug :wink:


#3

Since it’s the last argument to the macro, you could just use a tt matcher:

macro_rules! test_enum {
    ($name:ident, $input:expr, $($x:tt)*) => {
        #[test]
        fn $name() {
            match generate($input) {
                $($x)* => {},
                x @ Some(_) => panic!("Expected {:?}, got {:?}", $($x)*, x),
                None => panic!("Expected {:?}, got None", $($x)*)
            }
        }
    }
}

#4

Works like a charm, thanks! I just missed the fact that we can match on the sequence of token trees (and match for just one token tree wasn’t possible, when we pass Some(thing)).