I don't understand what happens when macros call macros

I have a small playground example here. I would expect to see "I have a burger!" twice on stdout, but I don't. Can anyone explain this strange behavior, and how to fix it?

I believe the problem is that you're using "Food::Burger" as both an expression and a token stream, which is confusing. So your code is basically equivalent to the following:

macro_rules! inner {
    (BLAH) => { println!("I have a burger!"); };
    ($food:expr) => { println!("I don't know what I got..."); };
}

macro_rules! outer {
    ($x:expr) => { inner!($x); };
}

enum Food { Burger }

fn main() {
    inner!(BLAH);
    outer!(Food::Burger);
}

Detailed explanation in The Little Book of Rust Macros chapter.

1 Like

The relevant behavior here is that an $:expr when passed to another macro acts as a single atomic token. You can see this in the following example in which $e, even though it comes in as three tokens 1 + 1, is successfully passed to inner! which accepts just a single token.

macro_rules! inner {
    ($single:tt) => { println!("okay"); };
}

macro_rules! outer {
    ($e:expr) => { inner!($e); }
}

fn main() {
    outer!(1 + 1);
}

By extension this means that a macro rule can do nothing to look inside of an $:expr. The following does not compile, even though $e comes in with a leading negative sign.

macro_rules! inner {
    (-$tt:tt) => { println!("okay"); };
}

macro_rules! outer {
    ($e:expr) => { inner!($e); }
}

fn main() {
    outer!(-x);
}

If it helps, you can think of an $:expr as being an expression surrounded by a set of "invisible" parentheses, and thus acts like a single $:tt just like an actually parenthesized expression would. In fact invisible delimiters is exactly how this stuff is represented in the procedural macro API by the Delimiter::None invisible set of delimiters.

pub enum Delimiter {
    Parenthesis,
    Brace,
    Bracket,
    None,
}
1 Like