[英]What is the simplest way to list fields of a struct in procedural derive Rust macros?
我想为结构编写一个派生宏,它将生成另一个具有相同字段但包装在Option
中的结构。
例如:
#[derive(Builder)]
pub struct Struct {
field_1: String,
field_2: Vec<String>,
field_3: f32,
}
生成的结构:
pub struct StructBuilder {
field_1: Option<String>,
field_2: Option<Vec<String>>,
field_3: Option<f32>,
}
我试过这样的事情:
#[proc_macro_derive(Builder)]
pub fn derive(input: TokenStream) -> TokenStream {
let _input = parse_macro_input!(input as DeriveInput);
let name = _input.ident;
let name_builder = Ident::new(&format!("{}Builder", name.to_string()), Span::call_site());
let Data::Struct(data) = _input.data;
let mut fields = proc_macro2::TokenStream::new();
let mut expanded = proc_macro2::TokenStream::new();
expanded.extend(quote!(pub struct #name_builder));
for field in data.fields {
let field_name = field.ident.unwrap();
let field_type = field.ty;
fields.extend(quote!(#field_name: Option<#field_type>,))
}
expanded.extend(TokenTree::from(Group::new(Delimiter::Brace, fields)));
expanded.into()
}
但它甚至不编译。
所以我想知道两件事:
extend
方法。{... }
之间插入生成的字段? 也不确定我使用expanded.extend(TokenTree::from(Group::new(Delimiter::Brace, fields)));
正确。 这也不起作用:expanded.extend(quote!(pub struct #name_builder {));
...
expanded.extend(quote!(}));
我很乐意收到任何反馈。 谢谢!
从_input.data
中提取data
的语法似乎略有偏差。 让我们先解决这个问题。
let syn::Data::Struct(data) = _input.data else {
unimplemented!()
};
我不推荐使用extend
方法。 而是使用quote!
创建顶级TokenStream
,然后在适当的地方将其他令牌流嵌套到其中。
让我们遍历fields
并为每个字段创建一个TokenStream
。
let fields = data.fields.iter().map(|f| {
let name = &f.ident;
let ty = &f.ty;
quote! {
#name: Option<#ty>
}
});
我们现在可以将字段令牌流嵌套到我们的顶级expanded
令牌 stream 中并返回它。
let expanded = quote! (
pub struct #name_builder {
#(#fields,)*
}
);
expanded.into()
这是完整的 function 供参考:
#[proc_macro_derive(Builder, attributes(builder))]
pub fn derive(input: TokenStream) -> TokenStream {
let _input = parse_macro_input!(input as DeriveInput);
let name = _input.ident;
let name_builder = Ident::new(&format!("{}Builder", name.to_string()), Span::call_site());
let data = if let syn::Data::Struct(data) = _input.data {
data
} else {
unimplemented!();
};
let fields = data.fields.iter().map(|f| {
let name = &f.ident;
let ty = &f.ty;
quote! {
#name: Option<#ty>
}
});
let expanded = quote! (
pub struct #name_builder {
#(#fields,)*
}
);
expanded.into()
}
输入:
#[derive(Builder)]
pub struct Struct {
field_1: String,
field_2: Vec<String>,
field_3: f32,
}
Output:
pub struct Struct {
field_1: String,
field_2: Vec<String>,
field_3: f32,
}
pub struct StructBuilder {
field_1: Option<String>,
field_2: Option<Vec<String>>,
field_3: Option<f32>,
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.