写点什么

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

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
用户头像

还未添加个人签名 2021-02-15 加入

还未添加个人简介

评论

发布
暂无评论
2022-11-14:rust语言,请使用过程宏给结构体AAA生成结构体AAABuilder和创建AAABuilder实例的方法。 宏使用如下: #[derive(Builder)] pub stru_rust_福大大架构师每日一题_InfoQ写作社区