Jun 10, 2022 rust过程宏 我们有这样一个结构体1struct TestStruct{2 a:String,3 b:String4}我们希望自定义一个Display。针对1TestStruct{a:String::from("aaa"),b:String::from("bbb")}形式化输出1a:aaa;b:bbb;利用过程宏可以做到这一点,编译时生成1std::fmt::Display的trait。“刚开始学习过程宏,简单记录自己怎么实现的和一些坑”直接上代码1extern crate proc_macro;2use proc_macro::TokenStream;34use quote::quote;5use syn::{parse_macro_input, Data, DeriveInput, Fields, Ident};67#[proc_macro_derive(format)]8pub fn derive_format(input: TokenStream) -> TokenStream {9 let input = parse_macro_input!(input as DeriveInput);10 let struct_name = input.ident;11 let struct_str = Ident::new("struct_str", struct_name.span());12 let expended = if let Data::Struct(r#struct) = input.data {13 if let Fields::Named(ref fields_name) = r#struct.fields {14 let get_selfs: Vec<_> = fields_name15 .named16 .iter()17 .map(|field| {18 let f = field.ident.as_ref().unwrap();1920 quote! {21 stringify!(#f),&self.#f22 }23 })24 .collect();2526 let format_string = "{}:{};".repeat(get_selfs.len());27 let format_literal =proc_macro2::Literal::string(format_string.as_str());28 let struct_fields = quote! {29 #(#get_selfs),*30 };3132 quote! {33 impl std::fmt::Display for #struct_name{34 fn fmt(&self,f:&mut std::fmt::Formatter)->std::fmt::Result{35 write!(f , #format_literal ,#struct_fields)36 }37 }38 }39 } else {40 panic!("sorry, may it's a complicated struct.")41 }42 } else {43 panic!("sorry, Show is not implemented for union or enumtype.")44 };45 expended.into()46}难点有两个:1.1proc_macro2::Literal1format!("{}",str);语句中格式控制字符串1"{}"是一个字面量1literal。quote内部使用的第三方库1proc_macro2,quote语法1#ident只接受1proc_macro2的1{TokenStream,Ident,....}。在上边代码中,我们需要将一个String变量转为一个字面量。这个一定要用1proc_macro2::Literal,用默认库1proc_macro::Literal,会报错mismatches types。要了命了才看出来这么个事2.1#(#get_selfs),* quote的语法糖1write!(f , #format_literal , #struct_fields) 写出1write!(f ,"{:?}" , (#(#get_selfs),*))这样可以过编译,最后这个参数会生成一个元组,这又没法自定义格式了。 写成1write!(f , #format_literal ,#(#get_selfs),*)又过不了编译。 所以用1let struct_fields = quote!{#(#get_selfs),*}套了一层。