I've boiled down an issue I'm seeing into this snippet:
macro_rules! test_impl {
(&mut $_:ty) => { 1 };
(&$_:ty) => { 2 };
($_:ty) => { 3 };
}
macro_rules! test {
($val: literal, $($t:ty), *) => { ($val $(, test_impl!($t))*) }
}
fn main() {
// I'm expecting (0, 3, 2, 1) here...
println!("{:?}", test!(0, f64, &f64, &mut f64));
}
When ends up printing out:
(0, 3, 3, 3)
It seems like the reference and mutable parts of the t
type aren't getting passed through. Am I understanding how this works wrong? Is there a way to pass the "reference/mut-ness" through the outer test!
macro and match it in test_impl!
?
Yes. Quoting the reference:
When forwarding a matched fragment to another macro-by-example, matchers in the second macro will see an opaque AST of the fragment type. The second macro can't use literal tokens to match the fragments in the matcher, only a fragment specifier of the same type. The
ident
,lifetime
, andtt
fragment types are an exception, and can be matched by literal tokens.
You cannot work around that with declarative macros: proc macros are able to remove those invisible fragments (they're represented as groups with invisible delimiters), but macro_rules!
cannot. The only way is to not capture them from the beginning and match them with tt
. Matching complex fragments, like ty
, can be hard and require tt munching.