Reputation: 951
Multiple examples I've seen suggest that this should be possible, but it is apparently not:
lib.rs
:
#![feature(trace_macros)]
#[macro_export]
macro_rules! inner_macro (
(f32) => {"float"};
);
#[macro_export]
macro_rules! outer_macro {
($T:ty) => {
inner_macro!($T)
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_nested() {
trace_macros!(true);
s1: String = String::from(outer_macro!(f32));
s2: String = String::from(inner_macro!(f32));
trace_macros!(false);
}
}
Running cargo test
gives the following output:
error: no rules expected the token `f32`
--> src/lib.rs:11:22
|
4 | / macro_rules! inner_macro (
5 | | (f32) => {"float"};
6 | | );
| |__- when calling this macro
...
11 | inner_macro!($T)
| ^^ no rules expected the token `f32`
...
21 | s1: String = String::from(outer_macro!(f32));
| ----------------- in this macro invocation
This is confusing, because there certainly appears to be a rule expecting the token f32
.
There are also notes from the expansion trace of the two macros. The first one does not work:
= note: expanding `outer_macro! { f32 }`
= note: to `inner_macro ! ( f32 )`
= note: expanding `inner_macro! { f32 }`
while the second one does:
= note: expanding `inner_macro! { f32 }`
= note: to `"float"`
Why does the first expansion of inner_macro!
fail, while the exact same expansion succeeds when it is not nested inside another macro?
Edit: if we perform the substitution manually, it works and gives the expected output:
macro_rules! unknown {
($T:ty) => {
inner_macro!(f32)
}
}
Upvotes: 3
Views: 163
Reputation: 951
After some more reading, it turns out this is an instance of a typical stumbling block. After being captured the first time, $T
takes the value of an AST Node. Substituting $T
will not emplace a token, it will emplace that AST Node. So what I expected to be something like this:
inner_macro!(`f32` [token])
was actually
inner_macro!(<Type>f32</Type>)
Unfortunately for users, the two invocations both get stringified the same way, to inner_macro! ( f32 )
.
The correct way to do this is to capture the token-to-be-substituted as a "token tree":
macro_rules! unknown {
($T:tt) => {
inner_macro!($T)
}
}
Upvotes: 3