当前位置: 首页 > news >正文

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这个方法,在方法中对各个字段进行处理。

所以用宏来操作,两个关键

  1. 结构体的名字
  2. 结构体字段的名字

获取这两个就可以了。行。

获取结构体的名字

定义派生宏

#[proc_macro_derive(MyDebug)]
pub fn my_debug(input: TokenStream) -> TokenStream {}

转换为派生宏能处理的语法解析树,获取名字

    let input=parse_macro_input!(input as DeriveInput);let name=&input.ident;

获取字段

  1. 判断是否是结构体
  2. 判断是否是命名结构体
  3. 返回字段

代码如下

    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") }

没问题。

http://www.xdnf.cn/news/1410697.html

相关文章:

  • 萌宝喂养日志-我用AI做喂养记录小程序1-原型设计
  • 中科大少年班记
  • 编程与数学 03-004 数据库系统概论 10_数据库的实施
  • 【GaussDB】排查应用高可用切换出现数据库整体卡顿及报错自治事务无法创建的问题
  • 基于JavaScript的智能合约平台(Agoric)
  • join怎么用
  • Spring Boot单体项目整合Nacos
  • STAR法则
  • C/C++ 高阶数据结构 —— 二叉搜索树(二叉排序树)
  • 【Linux】系统部分——ELF文件格式与动态库加载
  • 【系统分析师】高分论文:论大数据架构的应用
  • Linux系统比较两个​​已排序文件​​的实用工具之comm
  • 混合润滑表面接触刚度和接触阻尼模型
  • 计算机视觉与深度学习 | 低照度图像处理算法综述:发展、技术与趋势
  • ESP32_实验12_基于光敏传感器的停车场车辆计数系统
  • LeetCode 1855.下标对中的最大距离
  • 基于Python的OCR文字识别系统
  • More Effective C++ 条款19:理解临时对象的来源(Understand the Origin of Temporary Objects)
  • 地信/测绘/遥感就业岗位合集
  • Vue2 与 Vue3 路由钩子的区别及用法详解
  • 事件驱动架构新范式:FastEvent 让领域事件开发变得优雅
  • UVM APB 验证 VIP Agent 逻辑架构与数据流图
  • audioLDM模型代码阅读(三)——变分自编码器VAE
  • LeetCode100-160相交链表【链表介绍】
  • 基于AI的大模型在S2B2C商城小程序中的应用与定价策略自我评估
  • USBX移植(X是eXtended的意思)
  • 【python]变量及简单数据类型
  • Spring Data JPA 派生查询方法命名速查表
  • 平滑滤波器(Smooth Filter)的MATLAB与Verilog仿真设计与实现
  • linux内核trace_begin和trace_end使用分析