How to allow optional trailing commas in macros?
MacrosRustMacros 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.