Руст-рекурсивный макрос не работает для создания структуры - PullRequest
3 голосов
/ 06 марта 2019

Я пытаюсь написать макрос, который генерирует структуру в Rust.Этот макрос добавляет различные атрибуты Serde в структурные поля в зависимости от типа поля.Это конечная цель.

Сейчас я просто пытаюсь написать макрос, который использует другой макрос для генерации рекурсивного кода.

Вот как выглядит код:

macro_rules! f_list {
    ($fname: ident, $ftype: ty) => {
        pub $fname: $ftype,
    }
}

macro_rules! mk_str {
    ($sname: ident; $($fname: ident: $ftype: ty,)+) => {
        #[derive(Debug, Clone)]
        pub struct $sname {
            $(
                f_list!($fname, $ftype)
            )+
        }
    }
}

mk_str! {
    Yo;
    name: String,
}

fn main() {
    println!("{:?}", Yo { name: "yo".to_string() })
}

Этот код при запуске выдает ниже ошибку, которую я не могу понять.

error: expected `:`, found `!`
  --> src/main.rs:12:23
   |
12 |                   f_list!($fname, $ftype);
   |                         ^ expected `:`

Что здесь не так?

Вот игровая площадка ссылка

1 Ответ

1 голос
/ 06 марта 2019

При написании декларативных макросов (macro_rules!) важно понимать, что вывод макроса должен быть шаблоном, оператором, выражением, элементом или impl.По сути, вы должны думать о выводе как о чем-то, что может быть автономно, если говорить синтаксически.

В вашем коде макрос f_list, если бы он работал, вывел бы код наподобие

name1: type1,
name2: type2,
name3: type3,

Хотя это может быть частью объявления для структуры, оно само по себе не может быть чем-то отдельным.

Почему тогда ошибка в другом макросе?mk_str успешно расширяется до

#[derive(Debug, Clone)]
pub struct Yo {
    f_list!(name, String)
}

Однако синтаксический анализатор не ожидает макрос внутри объявления структуры.Внутренняя часть структуры не является шаблоном, оператором, выражением, элементом или impl.Таким образом, когда он видит !, он сдается и сообщает об ошибке.

Как вы можете это исправить?В этом конкретном примере f_list довольно избыточен.Вы можете просто заменить f_list!($fname, $ftype) на pub $fname: $ftype, в mk_str, и все будет работать как написано.Если это не работает для вашей цели, взгляните на Маленькая книга макросов ржавчины .У него есть несколько шаблонов для выполнения очень сложных вещей с макросами.Большая часть информации в этом ответе взята из раздела «Макросы в AST» .

...