Emulating an `:empty` macro matcher

It's a somewhat repeated desire to be able to expand macro code if a $()? captured anything but without actually capturing/expanding any matchers for the contents of the repetition. An oft proposed solution is some kind of $:empty matcher which could be inserted into the macro which both captures and expands to nothing, serving only to mark which repetitions to expand.

I just stumbled into a silly way to do this that works today. Specifically, I wrote a macro

macro_rules! impl_vector {{
    $(use Guest $(as $Guest:ident)?;)?
    $(const $N:ident: usize;)?
    type $Vector:ident;
    $($items:item)*
} => {
    const _: () = {
        $(const $N: usize = 2;)?
        $(use crate::ffi::vec2f::Guest $(as $Guest)?;)?
        type $Vector<T> = Vector2<T>;
        $($items)*
    };
    const _: () = {
        $(const $N: usize = 3;)?
        $(use crate::ffi::vec3f::Guest $(as $Guest)?;)?
        type $Vector<T> = Vector3<T>;
        $($items)*
    };
}}

Note specifically the $($(as $Guest)?)?. In an invocation that only writes use Guest;, the expansion expands the outer $()? but not the inner $()?; the inner $()? only really serves to determine what repetition the outer $()? pairs to, because I don't have any invocations which do use Guest as Alias; instead.

So the pattern is: if you find yourself wanting to capture $x:empty in a macro_rules! pattern, you can write something like $( __nobody_will_ever_use_this_identifier_i_hope $x:tt )? instead, "expanding" it to nothing with $($x)?. This will only cause errors if someone invokes your macro with the token __nobody_will_ever_use_this_identifier_i_hope whever your :empty shim matches.

6 Likes