#rust #macros #rules
#Ржавчина #макросы #Правила
Вопрос:
Я пытаюсь написать макрос, который генерирует структуру в 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:
При написании декларативных макросов ( 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,
in mk_str
, и это будет работать так, как написано. Если это не подходит для вашей цели, взгляните на The Little Book of Rust Macros. В нем есть несколько шаблонов для выполнения очень сложных действий с макросами. Большая часть информации в этом ответе взята из раздела «Макросы в AST».
Комментарии:
1. Спасибо! Это устраняет мои сомнения. Идея с
f_list
заключалась в том, чтобы иметь что-то вроде этого: macro_rules! f_list { ($fname: ident, Vec<$ftype: ty>) => { #[serde(по умолчанию)] pub $fname: Vec<$ftype>, } ($fname: ident, $ftype: ty) => { pub $fname: $ ftype, } }