How to match an argument in a signature with macro_rules?

From my understanding, a function argument in a signature has the form PATTERN: TYPE. However, macro_rules! does not seem to accept this:

macro_rules! id {
    ($p: pat: $t: ty) => {
        $p: $t
    };
}

fn foo(
    id!(x: u8)
) {}

gives the following error:

error: `$p:pat` is followed by `:`, which is not allowed for `pat` fragments

How can I resolve this? I tried a few other kinds instead of pat (pat_param, ident), but none of them seems to match either.

AFAIK you can't invoke macros for constructing a function parameter:

Because syntax extensions are parsed into the AST, they can only appear in positions where they are explicitly supported. Specifically syntax extensions can appear in place of the following:

  • Patterns
  • Statements
  • Expressions
  • Items(this includes impl items)
  • Types

Some things not on this list:

  • Identifiers
  • Match arms
  • Struct fields

There is absolutely, definitely no way to use syntax extensions in any position not on the first list.

(I did not notice you were trying to emit the output of the macro into a function parameter list. To work around that, you can wrap the whole function definition in a macro call and only alter the arguments. The rest of my reply only addresses the issue of not being able to match the input of the macro.)

Taking a look at follow-sets for macro fragment specifiers, only ty and path explicitly allow a following :, neither of which is helpful.

The reason for pat not allowing is, IIRC, to allow a potential type ascription syntax in the future without it being breaking (or needing the rename-over-edition dance that caused pat_param to exist).

Now for the solution, I don't see an "easy" way, these are the options that come to mind:

  • Exclude arbitrary patterns

    Don't allow arbitrary patterns, just identifiers. $p: ident : $t: ty

  • Don't separate them

    If you don't need to process the pattern and the type separately, you can match them in pairs with $($p_and_t: tt)+.

  • Use some alternative syntax

    For example, ($p: pat = $t: ty). This matches id!(x = u8).
    Confusing and ugly, but it Just Works™. You can choose any of =>, ,, =, if, or in to put after pat.

  • Use a tt muncher

    Use a tt muncher to parse tokens one by one until you hit a :. It is inefficient and a kind of wizardry I am not skilled in.

  • Switch to proc macros

    Probably overkill.

1 Like