Макросы с тремя необязательными параметрами, и один из них должен принимать список значений - PullRequest
1 голос
/ 17 апреля 2020

У меня есть структура с двумя необязательными полями, и я хотел бы предоставить специальный макрос для упрощения создания экземпляров.

Макрос должен принимать один обязательный аргумент и три необязательных аргумента. Последний необязательный аргумент должен принимать список значений. И я хотел бы использовать для этого ровно одну скороговорку.

Наконец-то я это сделал и все отлично работает:

#[derive(Debug)]
pub enum Variant {
    OptionOne,
    OptionTwo,
}

#[derive(Debug)]
pub struct TheStruct{
    string: String,
    first: Option<Variant>,
    second: Option<Variant>,
    numbers: Vec<u32>,
}

impl TheStruct {
    // doesn't matter
}

#[doc(hidden)]
#[macro_export]
macro_rules! the_macro_int {
    ($the_struct:ident, numbers, { $($w:expr),*$(,)*}) => {
        $($the_struct.numbers.push($w);)*
    };
    ($the_struct:ident, first, {$w:expr}) => {
        $the_struct.first = Some($w);
    };
    ($the_struct:ident, second, {$w:expr}) => {
        $the_struct.second = Some($w);
    };
}

#[macro_export]
macro_rules! the_macro {
    (string: $string:expr, $($kind:ident : $val:tt),*$(,)*) => {{
        let mut the_struct = $crate::TheStruct{
            string: $string,
            first: None,
            second: None,
            numbers: std::vec::Vec::new(),
        };
        $($crate::the_macro_int!(the_struct, $kind, $val);)*
        the_struct
    }};
}

fn main() {
    let the_struct = the_macro!(
        string: "Hello".to_owned(),
        first: { Variant::OptionOne },
        numbers: (1, 3, 4),
    );
    println!("the_struct:{:?}", the_struct);
}

Детская площадка

одна вещь делает меня несчастным: скобки в first: { Variant::OptionOne },.

Я пытался заменить свой шаблон на (string: $string:expr, $($kind:ident : $val:expr),*$(,)*), но он больше не работает для numebrs.

Возможно ли это переопределить шаблон для the_macro, чтобы first: Variant::OptionOne, был действительным, а numbers все еще может принимать список элементов? Вид скобок для numbers не имеет значения, но я не могу заменить список вещей только Ve c или чем-то подобным.

PS Я не ищу решение с несколькими шаблонами.

1 Ответ

1 голос
/ 17 апреля 2020

Есть две проблемы с вашим макросом:

  • Сначала он пытается сделать слишком много. Зачем инициализировать numbers с помощью numbers: (1, 3, 4), в то время как литералы массива используют [ / ], и вы можете инициализировать вектор только с 4 дополнительными символами?

    Давайте использовать более естественную запись numbers: vec![1, 3, 4]. Преимущество этого состоит в том, что пользователь может инициализировать поле уже существующим вектором.

  • Во-вторых, вам нужно использовать квадратные скобки вокруг { Variant::OptionOne }, так как вы использовали tt в качестве значения. , но Variant::OptionOne это не один tt, это 3. Если вы измените шаблон на более естественный expr, вы можете использовать Variant::OptionOne напрямую:

    (string: $string:expr, $($kind:ident : $val:expr),*$(,)*) => {{
    

( Постоянная ссылка на игровую площадку )


Однако я бы посоветовал вам не использовать такой макрос. Обычный способ справиться с инициализацией таких структур в Rust - это шаблон строителя .

...