Macro_rules matching different types

Hi,
I'm asking for your help with macro_rules, please check out this code to demonstrate my problem:
Playground

If I change ($tp:ty) to ($tp:tt) at the top, then it works for simple types like i32, but fails on Result<T> and complains about a missing rule for <.

It almost feel like I need to stringify!($tp) but I couldn't make it to work either.

Do you have a question? If you're asking why there is a difference between ty and tt, ty matches against any sequence of tokens that could represent a type (and will treat that token sequence as a type). Whereas tt will match against exactly one arbitrary token, which could be an identifier, keyword, or a symbol. So ty will match against Result<T>, but tt will only match Result, and then has some extra unparsed tokens which causes the error

1 Like

In this exercise one needs to apply a TT muncher pattern. Additionally the order needs to be changed to prevent infinite recursion, because everything matches $($tp:tt)*.

macro_rules! foo {
    (_ bool) => {
        println!("got bool");
    };
    (_ Result<i32>) => {
        println!("got Result<i32>");
    };
    (_ $tp:ty) => {
        println!("fallback to type: {}", stringify!($tp));
    };
    ($($tp:tt)*) => {
        foo!(_ $($tp)*);
    };
}

Ah, this tt which stands for TokenTree confused me into thinking that I only need a single tt to capture everything that being passed to the macro. Now I can see that tt is just a single symbol and to capture the whole tree we need $($all:tt)*
Thank you!

Also, regarding the initial question, once a macro input is captured in a metavariable whose kind is not :tt nor :ident, then it gets "wrapped in invisible parenthesis" which prevents it from matching any other literal rule.

foo!( bool ) receives the sequence of tokens `bool`, which can be parsed as a :ty and captured within one, as $tp := `⦑bool⦒`, (I am using and to represent the "invisible parenthesis"), which is then fed to an invocation of foo!(_ $tp), i.e.,

foo!( _ ⦑bool⦒ ), which receives the sequence of tokens `_`, `⦑bool⦒`, which does not match the rule expecting `_`, `bool`,, (nor the one with i32), and only matches the one with no hardcoded syntax: the nested _ $tp:ty capture (I don't know if it then gets yet another layer of invisible parens or if Rust is smart enough to know those are not needed anymore).

You can use the ::defile crate, which explains this phenomenon, to "remove these invisible parenthesis", but in general, the $(_:tt)* pattern with a muncher is the usual macro_rules! solution.

2 Likes

That explains why it didn't match, i.e. $tp:ty for bool != bool literal. I'm sure Rust does it for a reason, but it looks weird. It would feel so natural to capture a type with ty and then further match it against a concrete type literal.

Great info, thanks Yandors.

1 Like

Even though for :tys it does not seem necessary, for :expr these "invisible parenthesis" are what allow:

macro_rules! double {( $expr:expr $(,)? ) => (
    2 * $expr // no parens!
)}

to work as expected when doing double!(1 + 1): without the invisible parens, like the C preprocessor does, one would get 2 * 1 + 1 i.e., an incorrect 3,
but thanks to the parens, Rust does: 2 * ⦑1 + 1⦒ i.e., the correct 4 :slightly_smiling_face:

  • Granted, another design could have allowed both to expand to code with the right precedence, and to match against nested metavariables (e.g., when checking whether `⦑bool⦒` "equals" a literal `bool`, it could temporarily strip the "invisible parens" to perform the comparison).