目录
前言
如下代码
#[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") }
没问题。