Search code examples
macrosrustrust-macros

Match an underscore instead of ident in macro


I'm creating a macro that matches two expressions and an identifier. I would like to be able to ignore the identifier if it's not needed, but the compiler seems to complain if I use _ there.

My macro:

macro_rules! if_some {
    ($x:expr, $id:ident, $expr:expr) => {
        match $x {
            None => None,
            Some($id) => Some($expr),
        }
    };
}

What I'd like to do:

if_some!(obtain_an_option(), x, do_something_with(x))

and

if_some!(obtain_an_option(), _, do_something())

The second call fails.

I worked around it by defining a second macro if_some_! that doesn't receive an identifier (I could not use a second pattern either). I'm sure there's a way to say "here accept an identifier or just _.

Maybe there's already a macro/function for this (like Option::map now I think about it)... nevertheless it'd be nice to now.


Solution

  • Option::map seems to be the best solution for this particular problem, but when you really need a macro which expect both idents and _ as a pattern, you can also use the $p:pat fragment. The fragment of course accepts a broader range of patterns like (ref x, y), but typically this will be acceptable.

    macro_rules! if_some {
        ($x:expr, $p:pat, $expr:expr) => {
            match $x {
                None => None,
                Some($p) => Some($expr),
            }
        };
    }
    
    fn main() {
        println!("{:?}", if_some!(Some(12), x, x + 1)); // Some(13)
        println!("{:?}", if_some!(Some(12), _, 1)); // Some(1)
    }