Priority when matching type in macro_rules

I'm trying to write a macro that does different things depending on a given type.
Here is what I'm doing now:

macro_rules! print_type {
    (Option<Foo>) => {
        println!("Option of Foo");
    };
    (Option<$return_type: ty>) => {
        println!("Option of type");
    };
    ($return_type: ty) => {
        println!("simple type");
    };

    (
        $return_type: ty;
        $($rest: tt)*
    ) => {
        print_type!($return_type);
        print_type!($($rest)*);
    };
}

fn main() {
    // Expected output:
    // Option of Foo
    // simple type
    // Option of type
    print_type! {
        Option<Foo>;
        i32;
        Option<(i32, String)>
    }
}

For some reasons, only the last type seems to be matched correctly... Is there a way for each type to be correctly matched?
Thanks!

this recursion force the first captured meta variable to be parsed as ty token, which will never match the explicit Option<...> rules.

for example, the Option<Foo> pattern consists of 4 tokens (leaf of tt), namely:

  • ident(Option);
  • LESS_THAN operator;
  • ident(Foo);
  • GREATER_THAN operator;

OK, thank you! So I guess there's no way to do it other than this then?

macro_rules! print_type {
    (Option<Foo>) => {
        println!("Option of Foo");
    };
    (Option<$return_type: ty>) => {
        println!("Option of type");
    };
    ($return_type: ty) => {
        println!("simple type");
    };
}

fn main() {
    // Expected output:
    print_type!(Option<Foo>);
    print_type!(i32);
    print_type!(Option<(i32, String)>);
}

since you are expanding in statement context, you don't need "tail"-recursion, this should work:

macro_rules! print_type {
    (Option<Foo> $(; $($tt:tt)*)?) => {
        println!("Option of Foo");
        $(print_type!($($tt)*))?
    };
    (Option<$return_type: ty> $(; $($tt:tt)*)?) => {
        println!("Option of type");
        $(print_type!($($tt)*))?
    };
    ($return_type: ty $(; $($tt:tt)*)?) => {
        println!("simple type");
        $(print_type!($($tt)*))?
    };
}
4 Likes

This last post is indeed the proper solution to this problem :100:.

For more info, and/or for when the macro is too complex for repetition of the "munching" to be worth repeating in each branch, there is a helper crate and macro which lets you "unwrap" a :ty capture (and other such high level captures) so that its tokens be inspectable again:

2 Likes