How to filter out from a repetition pattern?

Viewed 66

I try to generate a struct from a macro looking like that:

decl_struct! {
    MyStruct {
        mut foo,
        const bar,
    }
}

The thing is that I want to filter out the fields marked as const. I have tried with the following code, but it does not function due to the fact that the outer macro is evaluated first:

macro_rules! decl_struct {
    {
        $name:ident {
            $( $kind:ident $member:ident, )*
        }
    } => {
        decl_struct!(@gen_struct $name { $( decl_struct!(@gen_struct_member $kind $member) )* } );
    };

    // Filters the `const` members out:
    (@gen_struct_member mut $member:ident) => ($member,);
    (@gen_struct_member const $member:ident) => ();
    
    // Generate the whole struct:
    {
        @gen_struct $name:ident {
            $( $member:ident, )*
        }
    } => {
        struct $name {
            $( $member:ident : u32 /*Dummy type*/, )*
        }
    };
}

decl_struct! {
    MyStruct {
        mut foo,
        const bar,
    }
}

Link to the playground.

What can be an alternative approach?

1 Answers

As documented in the reference:

Macros can expand to expressions, statements, items (including traits, impls, and foreign items), types, or patterns.

Notably, they can't expand to singular field definitions (as attempted by your @gen_struct_member invocations). However, for your particular problem, you can instead simplify to a single macro invocation with a bit of clever repetition:

macro_rules! decl_struct {
    {
        $name:ident {
            $( const $initial:ident, )*
            $(
                $( mut $mut:ident, )+
                $( const $const:ident, )*
            )*
        }
    } => {
        struct $name {$(
            $( $mut: u32, )+
        )*}
    };
}

Playground.

Related