Writing a rust macro repetition that may or may not initialize a struct field

Viewed 392

I'm trying to write a macro that lists a number of struct fields, but conditionally only creates initializer code from some fields in the list. Specifically, that might look something like this:

#[test]
fn test() {
    #[derive(PartialEq, Debug)]
    struct Foo {
        bar: usize,
    }

    let a = Foo {
        bar: 0,
    };

    let b = test!(Foo {
        bar: 0,
        #[nope] baz: 0,
    });

    assert_eq!(a, b);
}

Foo has no field baz, and the #[nope] should tell the macro to just do nothing for that field; the real macro would use baz in one but not another location, and there are also other "attributes" that need to be handled.

This is the base-line macro that accepts the invocation, but doesn't ignore baz:

macro_rules! test {
    (
        $struct:ty {
            $($(#[$modifier:ident])? $field:ident: $value:expr,)*
        }
    ) => {
        {
            // don't mind this syntax workaround
            type X = $struct;
            X {
                $($field: $value,)*
            }
        }
    };
}

Now the trick I know to have different rules for different repetitions is to delegate each repetition to a helper rule. Here's the same macro splitting the regular and nope variations:

macro_rules! test {
    (
        $struct:ty {
            $($(#[$modifier:ident])? $field:ident: $value:expr,)*
        }
    ) => {
        {
            type X = $struct;
            X {
                $($field: test!(@field $(#[$modifier])? $field: $value),)*
            }
        }
    };

    (
        @field $field:ident: $value:expr
    ) => {
        $value
    };

    (
        @field #[nope] $field:ident: $value:expr
    ) => {
        $value
    };
}

But that doesn't help here because I only delegate $value, not $field: $value,. But I can't delegate the whole thing because syntactically that's not one token tree(?) that a macro could produce/not produce.

Is there a way, using this or another trick, to achieve this? Preferably avoiding procedural macros, if possible.

0 Answers
Related