syn与quote的简单使用——实现debug
目录
前言
正文
参考
获取结构体的名字
获取字段
获取结构体字段的名字
最后的拼接
最终代码如下
测试一下
前言
如下代码
#[derive(Debug)]
struct Student{name: String,id:i32,options: Option<String>
}fn main() {let s=Student{name:"asdas".to_string(),id:1,options:Some("test".to_string())};println!("{:?}", s);
}
打印结果如下
Student { name: "asdas", id: 1, options: Some("test") }
自定义一个Mydebug这个派生宏实现这个过程。
正文
参考
这其实很简单,本质上就是实现Debug这个trait,使用代码实现这个trait,因此,如下
impl Debug for Student {fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {f.debug_struct("Student").field("name", &self.name).field("id", &self.id).field("options", &self.options).finish()}
}
实现Debug这个trait,需要实现fmt这个方法,在方法中对各个字段进行处理。
所以用宏来操作,两个关键
- 结构体的名字
- 结构体字段的名字
获取这两个就可以了。行。
获取结构体的名字
定义派生宏
#[proc_macro_derive(MyDebug)]
pub fn my_debug(input: TokenStream) -> TokenStream {}
转换为派生宏能处理的语法解析树,获取名字
let input=parse_macro_input!(input as DeriveInput);let name=&input.ident;
获取字段
- 判断是否是结构体
- 判断是否是命名结构体
- 返回字段
代码如下
let fields=match input.data{Data::Struct(data)=>{match data.fields{Fields::Named(fields)=>fields.named,_=>panic!("不是命名结构体")}},_=>panic!("不是结构体")};
获取结构体字段的名字
fields的类型是Punctuated<Field, Token![,]>,迭代获取其中的名字
let field_name=fields.iter().map(|field| field.ident.as_ref().unwrap());
最后的拼接
quote! {impl std::fmt::Debug for #name {fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {f.debug_struct(stringify!(#name))#(.field(stringify!(#field_name), &self.#field_name))*.finish()}}}.into()
#(...)*:对迭代器中的每个元素重复生成代码片段
很简单。
最终代码如下
use proc_macro::TokenStream;
use quote::quote;
use syn::DeriveInput;
use syn::{parse_macro_input, Data, Fields};
#[proc_macro_derive(MyDebug)]
pub fn my_debug(input: TokenStream) -> TokenStream {let input=parse_macro_input!(input as DeriveInput);let name=&input.ident;println!("结构体的名字:{}",name.to_string());let fields=match input.data{Data::Struct(data)=>{match data.fields{Fields::Named(fields)=>fields.named,_=>panic!("不是命名结构体")}},_=>panic!("不是结构体")};let field_name=fields.iter().map(|field| field.ident.as_ref().unwrap());quote! {impl std::fmt::Debug for #name {fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {f.debug_struct(stringify!(#name))#(.field(stringify!(#field_name), &self.#field_name))*.finish()}}}.into()}
测试一下
use debug_my::MyDebug;
#[derive(MyDebug)]
struct Student {name: String,id: i32,options: Option<String>,
}
fn main() {let s = Student {name: "asdas".to_string(),id: 1,options: Some("test".to_string()),};println!("{:?}", s);
}
结果如下
Student { name: "asdas", id: 1, options: Some("test") }
没问题。