4

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.

Silly Freak
  • 4,061
  • 1
  • 36
  • 58
  • guess you could use features in declarative macro. – Stargateur Oct 05 '20 at 17:12
  • 1
    My personal recommendation is to use proc-macro for a stuff like that. Please let me know if you're open for proc-macro solution then I can post it there as an answer. – MaxV Oct 05 '20 at 20:05
  • 1
    @MaxV I mean I have a workaround that will work for my use case (split all `#[nope]` fields off to a different repetition). Having to put all nope fields before the others doesn't hurt in my case. If you mean whether I'd review and accept a proc macro answer, definitely! – Silly Freak Oct 05 '20 at 20:52

0 Answers0