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);
}
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,
}