1

See the following:

macro_rules! foo {
    ($input:ident, $matcher:pat_param) => {
        match $input {
            $matcher => Some(x),
            _ => None
        }
    }
}

enum Foo {
    X(i32),
    Y
}

fn main() {
    let foo = Foo::X(1);
    let matched = foo!(foo, Foo::X(x));
    print!("{matched:?}");
}

A compiler error happens:

error[E0425]: cannot find value `x` in this scope
  --> src/main.rs:4:30
   |
4  |             $matcher => Some(x),
   |                              ^ not found in this scope
...
17 |     let matched = foo!(foo, Foo::X(x));
   |                   -------------------- in this macro invocation
   |
   = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0425`.
error: could not compile `playground` due to previous error

Not sure why this fails, since the macro use should effectively be:

let matched = match foo {
    Foo::X(x) => Some(x),
    _ => None
};

Why does pat_param accept an identifier and then cannot actually use it?

Kagami Sascha Rosylight
  • 1,412
  • 1
  • 14
  • 31

1 Answers1

2

Rust's declarative macros are (mostly) hygienic meaning they cannot access variables declared outside the macro and vice-versa. The goal is that code outside the macro invocation should not be affected by the code generated within the macro. Here is a contrived example, but hopefully shows that inadvertent declarations in the macro can't affect the behavior even if the identifiers happen to have the same name.

So, even if the tokens are generated as you show, there is a syntactical difference between the x in the pattern, and the x in the arm.

Here's a potential solution:

macro_rules! foo {
    ($input:ident, $matcher:pat_param => $value:expr) => {
        match $input {
            $matcher => Some($value),
            _ => None
        }
    }
}

let matched = foo!(foo, Foo::X(x) => x);

See:

kmdreko
  • 42,554
  • 6
  • 57
  • 106