1
2022-11-14:rust 语言,请使用过程宏给结构体 AAA 生成结构体 AAABuilder 和创建 AAABuilder 实例的方法。 宏使用如下: #[derive(Builder)] pub stru
作者:福大大架构师每日一题
- 2022-11-14 北京
本文字数:2013 字
阅读完需:约 7 分钟
![2022-11-14:rust语言,请使用过程宏给结构体AAA生成结构体AAABuilder和创建AAABuilder实例的方法。 宏使用如下: #[derive(Builder)] pub stru](https://static001.geekbang.org/infoq/c2/c2d9c605050459b2d5faa876c360b61b.png)
2022-11-14:rust 语言,请使用过程宏给结构体 AAA 生成结构体 AAABuilder 和创建 AAABuilder 实例的方法。宏使用如下:
#[derive(Builder)]pub struct AAA { a: String, b: i32, c: f64, d: Vec<bool>,}
复制代码
宏展开后变成如下代码:
pub struct AAA { a: String, b: i32, c: f64, d: Vec<bool>,}pub struct AAABuilder { a: std::option::Option<String>, b: std::option::Option<i32>, c: std::option::Option<f64>, d: std::option::Option<Vec<bool>>,}impl AAA { pub fn builder() -> AAABuilder { AAABuilder { a: std::option::Option::None, b: std::option::Option::None, c: std::option::Option::None, d: std::option::Option::None, } }}
复制代码
答案 2022-11-14:
这题没啥技巧,自然智慧即可。
代码用 rust 编写。代码如下:
// builder/src/lib.rsuse proc_macro::TokenStream;use quote;use syn::spanned::Spanned;#[proc_macro_derive(Builder)]pub fn derive(input: TokenStream) -> TokenStream { let st = syn::parse_macro_input!(input as syn::DeriveInput); match expand_code(&st) { Ok(token_stream) => token_stream.into(), Err(e) => e.to_compile_error().into(), }}
fn get_fields( st: &syn::DeriveInput,) -> syn::Result<&syn::punctuated::Punctuated<syn::Field, syn::Token![,]>> { if let syn::Data::Struct(syn::DataStruct { fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }), .. }) = st.data { return Ok(named); }; Err(syn::Error::new_spanned(st, "必须定义在结构体上"))}
fn gen_fields(st: &syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream> { let fields = get_fields(st)?; let idents: Vec<_> = fields.iter().map(|f| &f.ident).collect(); let types: Vec<_> = fields.iter().map(|f| &f.ty).collect(); let ret = quote::quote!( #( #idents: std::option::Option<#types>),* ); return Ok(ret);}
fn gen_init_clauses(st: &syn::DeriveInput) -> syn::Result<Vec<proc_macro2::TokenStream>> { let fields = get_fields(st)?; let init_cluase: Vec<_> = fields .iter() .map(|f| { let ident = &f.ident; quote::quote!( #ident: std::option::Option::None ) }) .collect(); Ok(init_cluase)}
fn expand_code(st: &syn::DeriveInput) -> syn::Result<proc_macro2::TokenStream> { let struct_name_literal = st.ident.to_string(); let builder_name_literal = format!("{}Builder", struct_name_literal); let builder_name_ident = syn::Ident::new(&builder_name_literal, st.span()); let struct_ident = &st.ident; let builder_struct_field_def = gen_fields(st)?; let builder_struct_factory_init_clauses = gen_init_clauses(st)?; let ret = quote::quote!( pub struct #builder_name_ident{ #builder_struct_field_def }
impl #struct_ident { pub fn builder() -> #builder_name_ident { #builder_name_ident { #(#builder_struct_factory_init_clauses),* } } } ); Ok(ret)}
复制代码
# builder.Cargo.toml[package]name = "derive_builder"version = "0.0.0"autotests = falseedition = "2021"publish = false
[lib]proc-macro = true
[[test]]name = "tests"path = "tests/progress.rs"
[dev-dependencies]trybuild = { version = "1.0.49", features = ["diff"] }
[dependencies]syn = {version="1.0",features=["extra-traits"]}proc-macro2 = {version="1.0"}quote = {version="1.0"}
复制代码
use derive_builder::Builder;
#[derive(Builder)]pub struct AAA { a: String, b: i32, c: f64, d: Vec<bool>,}
fn main() { let builder = AAA::builder();
let _ = builder;}
复制代码
# Cargo.toml[package]name = "proc-macro-workshop"version = "0.0.0"edition = "2021"publish = false
[workspace]
[[bin]]name = "workshop"path = "main.rs"
[dependencies]bitfield = { path = "bitfield" }derive_builder = { path = "builder" }derive_debug = { path = "debug" }seq = { path = "seq" }sorted = { path = "sorted" }
复制代码
敲 cargo expand 命令后,打印如下:
划线
评论
复制
发布于: 刚刚阅读数: 3
版权声明: 本文为 InfoQ 作者【福大大架构师每日一题】的原创文章。
原文链接:【http://xie.infoq.cn/article/9ffe3cc68644779041960467e】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
福大大架构师每日一题
关注
还未添加个人签名 2021-02-15 加入
还未添加个人简介









评论