0

I apparently don't understand how scoping works with regards to macros. I'm trying to write a macro using the TT muncher pattern. I think I need to have an initial matching arm which declares a variable, then munching arms which build it as they consume sequences of tokens.

Here's an oversimplified thing that works, following the general pattern:

macro_rules! simple_muncher {
    (__recursive) => {};
    (__recursive $l : literal $($tail:tt)*) => {
        {
        println!("I see {}", $l);
        simple_muncher!(__recursive $($tail)*);
        }
    };
    ($($tail:tt)*) => {
        {
        println!("Starting off!");
        simple_muncher!(__recursive $($tail)*)
        }
    }
}

And here's one that doesn't work, because it doesn't see v as being in scope:


macro_rules! scoped_muncher {
    (__recursive) => {};
    (__recursive $l : literal $($tail:tt)*) => {
        {
        v.push(stringify!($l));
        simple_muncher!(__recursive $($tail)*);
        }
    };
    ($($tail:tt)*) => {
        {
        println!("Starting off!");
        let mut v : Vec<&str> = Vec::<&str>::new();
        scoped_muncher!(__recursive  $($tail)*);
        println!("Vector contains: {:?}", v)
        }
    }
}

But why doesn't it? For a simple call (scoped_muncher!(1), say) shouldn't it be outputting something like:

{

        println!("Starting off!");
        let mut v : Vec<&str> = Vec::<&str>::new();
        {
            v.push(stringify!(1));
        }
        println!("Vector contains: {:?}", v)
}

which is perfectly legal. Why is the macro saying that there's no v in scope?

What's the rhyme or reason to what macros can or can't see? Bonus question: even in the working version, it looks like I'll be generating a lot of nested scopes. Is there any way around that? There seem to be unclear rules of when you need the extra {} block within the RHS of a macro arm, and when you don't.

Edward Peters
  • 3,623
  • 2
  • 16
  • 39
  • 3
    Does this answer your question? https://stackoverflow.com/questions/70691520/is-there-a-way-to-access-variables-defined-in-rust-macros-from-a-passed-expr – kmdreko Jan 03 '23 at 02:03
  • More or less - it answers the first part, and I think I know a workaround (declaring v in the first arm, then having the `__recurse` arms take it as a parameter `$v : ident` - does that sound right?) I'm still curious about why the `{}` nesting works the way it does, if there are costs to generating deeply nested blocks, and if there's any way around it. – Edward Peters Jan 03 '23 at 03:21
  • @kmdreko And I think I figured that out too - if the macro (as a whole, not the macro arm) is expected to evaluate as an expr, then every arm of it must do so, and thus has to be a block. So you can make the flat structure I want, but you have to do so by having two different macros (not one macro with multiple arms), with the "lists of statement" arms in one macro, and the returning macro in another. Does that sound right? – Edward Peters Jan 03 '23 at 03:30
  • 1
    Small nitpick: the usual convention for markers used in recursive macros is to prefix them with `@` (e.g. to use `@recursive` instead of `__recursive`) because this ensures that these markers will never collide with an existing identifier. – Jmb Jan 03 '23 at 08:07
  • @Jmb thanks for the tip, but why does that ensure they won't collide? – Edward Peters Jan 03 '23 at 13:19
  • 1
    @EdwardPeters because `@recursive` is not a valid identifier, so there is no way that the user will ever want to pass a variable named `@recursive` to the macro, whereas they may have a variable named `__recursive` and want to pass it. – Jmb Jan 03 '23 at 13:23

0 Answers0