Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to allow optional trailing commas in macros?

Tags:

macros

rust

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, }); 
like image 622
user1244932 Avatar asked Mar 31 '17 14:03

user1244932


2 Answers

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),* $(,)* }) => {  //                                 ^^^^^ 
like image 135
Shepmaster Avatar answered Oct 17 '22 02:10

Shepmaster


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.

like image 31
AldaronLau Avatar answered Oct 17 '22 03:10

AldaronLau