Rust Macros: Repeat n times based on number of arguments without using the actual argument

Viewed 53

Is it possible to repeat something n times based on the number of arguments without using the actual argument? My usecase would be to implement an Enum variant, that takes 1 or more type params as well as an implementation that matches the enum variant using wildcards. So the number of wildcards will depend on the number of arguments provided. Example:

macro_rules! impl_enum {
    ($name: ident, $($params: ty)+, $val:expr) => {
        enum MyEnum {
            $name($($params)+,)
        }
        impl MyEnum {
            fn get_val(self) {
                match self {
                    MyEnum::$name(??????) => $val
                }
            }
        }
    }
}

Now I would like the output of

impl_enum!(Variant1, Type1 Type2 Type3, 42);

to be

            enum MyEnum {
                Variant1(Type1, Type2, Type3),
            }
            impl MyEnum {
                fn get_val(self) {
                    match self {
                        MyEnum::Variant1(_,_,_) => 42,
                    }
                }
            }

Is this possible?

2 Answers

You can do this with a macro that converts a ty to an underscore:

macro_rules! type_as_underscore {
    ( $t: ty ) => { _ };
}

Now, using this and fixing up a few other errors in your macro:

macro_rules! impl_enum {
    ($name: ident, $($params: ty)+, $val:expr) => {
        enum MyEnum {
            $name($($params),+)
        }
        impl MyEnum {
            fn get_val(self) {
                match self {
                    MyEnum::$name($(type_as_underscore!($params)),+) => $val
                };
            }
        }
    };
}

On nightly, this is supported by the experimental feature macro_metavar_expr. You can add ${ignore(metavariable)} to a repeat expression to mark that you want it to be based on metavariable, but without actually emitting it:

#![feature(macro_metavar_expr)]

macro_rules! impl_enum {
    ($name: ident, $($params: ty)+, $val:expr) => {
        enum MyEnum {
            $name($($params),+)
        }
        impl MyEnum {
            fn get_val(self) {
                match self {
                    MyEnum::$name($( ${ignore(params)} _ ),*) => $val
                }
            }
        }
    }
}
Related