How to allow optional trailing commas in macros?

MacrosRust

Macros Problem Overview


Here's a synthetic example of what I want:

macro_rules! define_enum {
    ($Name:ident { $($Variant:ident),* }) => {
        pub enum $Name {
            None,
            $($Variant),*,
        }
    }
}

define_enum!(Foo { A, B });

This code compiles, but if add a comma to it:

define_enum!(Foo { A, B, });
//                     ^

The compilation fails. I can fix it with:

($Name:ident { $($Variant:ident,)* })
//                             ^

but then define_enum!(Foo { A, B }); fails,

How should I write a macro to handle both cases:

define_enum!(Foo { A, B });
define_enum!(Foo { A, B, });

Macros Solutions


Solution 1 - Macros

Handle both cases

You can handle both cases by... handling both cases:

macro_rules! define_enum {
    ($Name:ident { $($Variant:ident,)* }) => {
        pub enum $Name {
            None,
            $($Variant),*,
        }
    };
    ($Name:ident { $($Variant:ident),* }) => {
        define_enum!($Name { $($Variant,)* });
    };
}

define_enum!(Foo1 { A, B });
define_enum!(Foo2 { A, B, });

fn main() {}

We've moved the main implementation to the version that expects the trailing comma. We then added a second clause that matches the case with the missing comma and rewrites it to the version with a comma.

Make the comma optional

DK. points out an alternative, making the trailing comma itself optional.

This avoids the need to delegate from one implementation to the other.

Rust 1.32

You can use the ? macro repeater to write this and disallow multiple trailing commas:

($Name:ident { $($Variant:ident),* $(,)? }) => { 
//                                 ^^^^^

Previous versions

This allows multiple trailing commas:

($Name:ident { $($Variant:ident),* $(,)* }) => { 
//                                 ^^^^^

Solution 2 - Macros

Change the line

($Name:ident { $($Variant:ident),* }) => {

to

($Name:ident { $($Variant:ident),* $(,)? }) => {

to add an optional comma at the end. This works in stable Rust / 2018 edition. This syntax also works for other separators like a semicolon.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser1244932View Question on Stackoverflow
Solution 1 - MacrosShepmasterView Answer on Stackoverflow
Solution 2 - MacrosAldaronLauView Answer on Stackoverflow