How to match generics in macros?

In a macro, let’s suppose I wanted to match the following input:

foo! {
    x<T: Debug + Add<T>> where T: Display

And produce something like so:

fn x<T: Debug + Add<T>>() where T: Display {}

I could try the following:

macro_rules! foo {
    ($name:ident<$($gen:tt)*> where $($wh:tt)*) => {
        fn $name<$($gen)*>() where $($wh)* {}

But that unfortunately fails.

Say that I add a space between the two >s to make sure that it’s not parsing as >> and instead as > >. Now it complains that

   Compiling playground v0.0.1 (/playground)
error: local ambiguity: multiple parsing options: built-in NTs tt ('gen') or 1 other option.
 --> src/
9 | foo!(x<T: Debug + Add<T> > where T: Display);
  |                        ^

Which isn’t any better.
Then I learned, that I could instead use something like the following to match without the +s separating them:

macro_rules! foo {
    ($name:ident<$($gen:ident$(: $cons:path)?)*> where $($lhs:ty : $rhs:path)*) => {
        fn $name<$($gen $(:$cons)?)*>() where $($lhs: $rhs)* {}

foo!(x<T: Debug> where T: Display);
foo!(y<T: Add<T>> where T: Display);

But, as can be seen in the example, we must drop the + Add<T> or the + Debug. This is because we cannot use + as a separator in a repetition ($($x:ident)+* would match a* and a b* but not a+b) and using a simple ty doesn’t work. …not to mention that a path or ty can’t be followed by a +.

I could try to expand the entire generics syntax using a convoluted macro, but that seems cumbersome and a bit of a rabbit hole.

Therefore I’m fresh out of ideas and was wondering what other people have done to solve this issue, if anyone else has even tried to match on generics before.


I don’t know, but the cop-out answer is that a procedural macro is probably much easier.

1 Like