Как разрешить необязательные конечные запятые в макросах?
Вот синтетический пример того, что я хочу:
macro_rules! define_enum {
($Name:ident { $($Variant:ident),* }) => {
pub enum $Name {
None,
$($Variant),*,
}
}
}
define_enum!(Foo { A, B });
Этот код компилируется, но если добавить к нему запятую:
define_enum!(Foo { A, B, });
// ^
Компиляция не удалась. Я могу это исправить с помощью:
($Name:ident { $($Variant:ident,)* })
// ^
но потом define_enum!(Foo { A, B });
выходит из строя,
Как мне написать макрос для обработки обоих случаев:
define_enum!(Foo { A, B });
define_enum!(Foo { A, B, });
3 ответа
Вы можете обрабатывать оба случая, обрабатывая оба случая:
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() {}
Мы переместили основную реализацию в версию, которая ожидает завершающую запятую. Затем мы добавили второе предложение, которое соответствует регистру с отсутствующей запятой, и переписали его в версию с запятой.
DK. указывает альтернативу, делая конечную запятую необязательной:
($Name:ident { $($Variant:ident),* $(,)* }) => {
Это позволяет избежать необходимости делегировать от одной реализации к другой.
В ночных версиях Rust вы можете использовать macro_at_most_once_rep
особенность, чтобы написать это более очевидным способом и запретить множественные запятые:
($Name:ident { $($Variant:ident),* $(,)? }) => {
// ^
Изменить линию
($Name:ident { $($Variant:ident),* }) => {
в
($Name:ident { $($Variant:ident),* $(,)? }) => {
добавить необязательную запятую в конце. Это работает в стабильной версии Rust / 2018. Этот синтаксис также работает для других разделителей, таких как точка с запятой.
Другой вариант (если вы используете Incremental TT Muncher) — использовать необязательный захват разделителя + оставшиеся токены. Используя немного другой пример:
macro_rules! example {
($name:ident = $value:expr $(, $($tts:tt)*)?) => {
println!("{} = {}", stringify!($name), $value);
example!($($($tts)*)?);
};
($value:expr $(, $($tts:tt)*)?) => {
println!("{}", $value);
example!($($($tts)*)?);
};
() => {};
}
Затем этот макрос можно вызвать, например, как:
example! { A = 1, "B", C = 3, "D" }
а замыкающая запятая может быть либо включена, либо опущена.