5

I thought "hygiene" would prevent collisions between Xs defined within my macro m! but that turned out not to be the case. What am I misunderstanding?

macro_rules! m {
    ($e:expr) => {
        const X: i32 = $e;
    };
}

m!(0);
m!(1);

fn main() {
    m!(2);
    m!(3);
}

Playground

Error message:

error[E0428]: the name `X` is defined multiple times
 --> src/main.rs:3:9
  |
3 |         const X: i32 = $e;
  |         ^^^^^^^^^^^^^^^^^^
  |         |
  |         `X` redefined here
  |         previous definition of the value `X` here
...
7 | m!(0);
  | ------ in this macro invocation
  |
  = note: `X` must be defined only once in the value namespace of this module
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
nodakai
  • 7,773
  • 3
  • 30
  • 60

1 Answers1

3

From The Rust Programming Language (first edition)'s section on macro hygiene:

This [i.e. renaming] holds for let bindings and loop labels, but not for items

The Rust reference defines items:

An item is a component of a crate. Items are organized within a crate by a nested set of modules. Every crate has a single "outermost" anonymous module; all further items within the crate have paths within the module tree of the crate.

Items are entirely determined at compile-time, generally remain fixed during execution, and may reside in read-only memory.

There are several kinds of items:

  • modules
  • extern crate declarations
  • use declarations
  • function definitions
  • type definitions
  • struct definitions
  • enumeration definitions
  • union definitions
  • constant items
  • static items
  • trait definitions
  • implementations
  • extern blocks

This makes sense: if you you introduce an item in a macro you presumably want to actually use it from other items/modules/crates (and thus outside the macro), but you can't if you don't know its name, so the compiler can't rename it.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • Thanks. So it's by design. "if you you introduce an item in a macro you presumably want to actually use it," Well, I guess that's not how we are supposed to use what's advertised as a hygienic macro system... – nodakai Mar 27 '16 at 05:34
  • @nodakai Sorry, I missed "outside the macro" there. In your case, if you put `println!("X = {}", X);` after the last `m!(3)`, what do you expect it to print? – Alexey Romanov Mar 27 '16 at 06:50
  • I'd expect a "unknown variable X" error when I heard Rust macros were hygienic. – nodakai Mar 27 '16 at 07:04
  • And that _is_ what you'd get for a variable (`let` binding), but `const` doesn't introduce one. – Alexey Romanov Mar 27 '16 at 12:13