5

I would like to compile code similiar to this minimal test case:

macro_rules! why {
    ( [ $saved:ident ] $body:block ) => {
        let $saved = 3;
        $body
        let _a = $saved;
    }
}

fn bar() {
    why!([saved] {
    });
}

fn main() {
}

When I try to compile it, I get the following error:

src/main.rs:10:20: 10:21 error: unresolved name `saved` [E0425]
src/main.rs:10         why!([saved] {
                                  ^
src/main.rs:10:9: 11:12 note: in this expansion of why! (defined in src/main.rs)
src/main.rs:10:20: 10:21 help: run `rustc --explain E0425` to see a detailed explanation

Other macros that introduce variables work; what's the problem here?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366

2 Answers2

7

This is because macro_rules! is kinda broken when it comes to macros that expand to statements.

The problem is basically that it considers each statement independently for the purposes of hygiene. In other words, the third statement literally cannot see the binding defined on the first line.

In some cases, you can work around this by wrapping the statements in a block:

macro_rules! why {
    ( [ $saved:ident ] $body:block ) => {
        {
            let $saved = 3;
            $body
            let _a = $saved;
        }
    }
}
DK.
  • 55,277
  • 5
  • 189
  • 162
1

Your fn bar doesn't have anything named saved in scope at the invocation site.

jwilm
  • 455
  • 4
  • 11
  • When I introduce a line `let saved = 3` in bar() before the macro, it compiles, even though te saved inside the macro block has nothing to do with the one outside (except for shadowing it). – Philipp Matthias Schäfer Jan 23 '16 at 15:36
  • That's right. Rust's macros are [_hygienic_](https://doc.rust-lang.org/book/macros.html#hygiene). One of the repercussions of macro hygiene is the inability to introduce variables like that into the calling scope. You _can_ return values, however. – jwilm Jan 23 '16 at 15:54
  • This is not entirely true, as your link says: "This holds for let bindings and loop labels, but not for items. So the following code does compile:". See for example https://github.com/rust-lang-nursery/lazy-static.rs . – Philipp Matthias Schäfer Jan 25 '16 at 06:56