二十、Rust AOP 切面增强

  用过 java spring 的同学,应该会对 AspectJ 的 前置、后置、环绕 增强 念念不忘,巧了 rust 也有类似能力,稍显不同的是,为了向 “零成本抽象” 靠齐,Rust 的 “增强” 是在编译期 完成的。

  编译期生成,则离不开 “宏”,本篇使用 “过程宏” 来实现 AOP 的代理增强,逻辑比较简单,记录函数的运行时间。

重要的点

  • Rust 过程宏,要求放入独立的 “项目” or “包” 中。
  • 原因:过程宏必须先被编译才能使用,和项目代码放在一起,也要先编译 “宏”,但 rust 编译单元是 “包”,而无法做到这一点。

创建项目

  • 创建一个 lib 项目
cargo new elapsed --lib
  • 将项目 描述为 macro(宏)项目
[lib]
proc-macro = true[dependencies]
quote = "1"
syn = { version = "2.0.58", features = ["full"] }
  • syn:解析语法树(AST)、及各种语法构成;
  • quote:使用 解析结果,生成rust代码,实现想要的功能;

实现“宏增强”逻辑

  • Rust 继续要求:宏声明,必须在 crate root 下,即 lib.rs 中。

    但为使结构清晰,可以在 crate root ( src/lib.rs ) 中只做声明,而在其他 mod 中具体实现;

  • elapsed/src/lib.rs

use proc_macro::TokenStream;mod elapsed;#[proc_macro_attribute]
#[cfg(not(test))]
pub fn elapsed(args: TokenStream, func: TokenStream) -> TokenStream {elapsed::elapsed(args, func)
}
  • elapsed/src/elapsed.rs
use proc_macro::TokenStream;
use quote::quote;
use syn::ItemFn;
use syn::parse_macro_input;pub(crate) fn elapsed(_attr: TokenStream, func: TokenStream) -> TokenStream {let func = parse_macro_input!(func as ItemFn);let func_vis = &func.vis;     // like publet func_block = &func.block; // { some statement or expression here }let func_decl = func.sig;let func_name = &func_decl.ident;  // function namelet func_generics = &func_decl.generics;let func_inputs = &func_decl.inputs;let func_output = &func_decl.output;let caller = quote! {#func_vis fn #func_name #func_generics(#func_inputs) #func_output {use std::time;let start = time::Instant::now();#func_blockprintln!("time cost {:?}", start.elapsed());}};caller.into()
}

简单解释:

  • pub(crate) 指定该函数仅在当前crate中可见;
  • parse_macro_input!(func as ItemFn) 将 AST Token 转为函数定义 func

随后获取了函数的各个部分:

  • vis:可见性;
  • block:函数体;
  • func.sig:函数签名:
    • ident:函数名;
    • generics:函数声明的范型;
    • inputs:函数入参;
    • output:函数出参;

最后,通过 quote! 创建了一个新的 rust 代码块;

  • caller.into 将结果,转换为编译器可识别的内容:TokenStream

测试运行

  • 到另外的工程,引入本项目,并使用该过程宏。
[dependencies]
elapsed = { path = "../elapsed" }
  • 本例中,该 #[elapsed] 只能加在同步方法上,加在异步方法上,会破坏方法的异步性,导致报错。
#[elapsed]
fn demo(t: u64) {let secs = Duration::from_secs(t);thread::sleep(secs);
}fn main() {demo(4);demo(2);
}
  • 运行后,可得:
time cost 4.004699342s
time cost 2.003885116s
  • cargo-expand:可查看 方法 经宏展开 替换后的样子
cargo install cargo-expand
cargo expand
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2018::*;
#[macro_use]
extern crate std;
use my_macro::elapsed;
use std::thread;
use std::time::Duration;
fn demo(t: u64) {use std::time;let start = time::Instant::now();{let secs = Duration::from_secs(t);thread::sleep(secs);}{::std::io::_print(::core::fmt::Arguments::new_v1(&["time cost ", "\n"],&[::core::fmt::ArgumentV1::new_debug(&start.elapsed())],),);};
}
fn main() {demo(4);demo(2);
}

  

总结

  通过 syn 和 quote,可以在编译期,操纵整个 rust 代码的 AST 树,为功能编写、甚至框架封装,提供了更多可能 !

  

  完事,拜了个 bye ~

  
  

参考资料

  • 如何编写一个过程宏(proc-macro)
  • Rust过程宏系列教程 | Proc Macro Workshop 之 Builder 实现
  • https://github.com/dtolnay/proc-macro-workshop/
  • Macro 宏编程
  • https://jasonkayzk.github.io/2022/11/25/Rust%E5%8F%8D%E5%B0%84%E4%B9%8B%E8%BF%87%E7%A8%8B%E5%AE%8F/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/793495.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数字信号处理实验---FFT分析

一、题目: 二、实验要求: 1、绘制图形时,尽量选用已经提供的函数。 2、所有的图形,需要加上横坐标、纵坐标以及标题的说明。 3、将设计的程序保存为脚本文件,在实验报告中,需写出程序语句。 4、Matlab程…

ECC升级S/4HANA“沙箱模拟方案包“:更适用于升级环境复杂、体量大的客户

“作为一家业务体量大的公司,分支机构众多、数据复杂,想从ECC升级会有什么风险?”面对技术更新换代、公司业务不断扩展、网络安全问题的日益突出,将ECC升级到S/4HANA是企业的当务之急。为解决客户的顾虑,无论是对于历史…

51单片机入门_江协科技_19~20_OB记录的笔记

19. 串口通讯 19.1. 串口介绍: •串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。 •单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大的…

设计模式总结-建造者模式

建造者模式 模式动机模式定义模式结构模式分析建造者模式实例与解析实例:KFC套餐 模式动机 无论是在现实世界中还是在软件系统中,都存在一些复杂的对象,它们拥有多个组成部分,如汽车,它包括车轮、方向盘、发送机等各种…

使用labelImg标注yolov5数据集并在kaggle中使用yolov5

使用labelImg标注yolov5数据集并在kaggle中使用yolov5 文章目录 前言一、labelImg标注1.1. 下载exe文件1.2. labelImg 下载(源码)1.3. 环境配置1.4. 使用1.4.1. 设置1.4.2. 导入图片并标注 二、在kaggle中使用2.1. 下载源码2.2. 编辑配置文件2.3. 将压缩…

[蓝桥杯 2017 国 C] 合根植物

[蓝桥杯 2017 国 C] 合根植物 题目描述 w 星球的一个种植园,被分成 m n m \times n mn 个小格子(东西方向 m m m 行,南北方向 n n n 列)。每个格子里种了一株合根植物。 这种植物有个特点,它的根可能会沿着南北…

StarRocks使用Minio备份和还原

1.安装minio Centos7安装minio-CSDN博客 minio api端口&#xff1a;9090 下文用到这个端口 必须提前创建好桶: packfdv5 名称自定义和后面对上就可以 2.创建备份仓库 格式&#xff1a; CREATE REPOSITORY <repository_name> WITH BROKER ON LOCATION "s3a:/…

支付类漏洞挖掘技巧总结

前言&#xff1a; 支付类逻辑漏洞在漏洞挖掘中是常常出现的问题之一。此类漏洞挖掘思路多&#xff0c;奖励高&#xff0c;是炙手可热的漏洞。此篇文章主要围绕挖掘支付逻辑漏洞时的一些思路分享。 支付逻辑漏洞成因&#xff1a; 支付漏洞可能由以下原因造成&#xff1a; 前…

mybatis流式游标查询-导出DB大数据量查询OOM问题

问题场景 Mysql数据处理类型分以下三种 com.mysql.cj.protocol.a.result.ResultsetRowsStatic&#xff1a;普通查询&#xff0c;将结果集一次性全部拉取到内存 com.mysql.cj.protocol.a.result.ResultsetRowsCursor&#xff1a;游标查询&#xff0c;将结果集分批拉取到内存&…

【Windows11】cmd下运行python弹出windows应用商店解决方案

【Windows11 】cmd下运行python弹出windows应用商店解决方案 大家好 我是寸铁&#x1f44a; 总结了一篇【Windows11 】cmd下运行python弹出windows应用商店解决方案✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 前言 今天在安装python时&#xff0c;在命令行窗口输入如下命令&a…

tomcat中的web项目配置指引

文章目录 目录结构I server.xml 配置文件1.1 Host标签1.2 contex标签1.3 server.xml 的端口配置1.4 appBase和docBase的区别1.5 Engine标签1.6 Connector标签II Tomcat应用的配置2.1 配置虚拟路径2.2 配置连接数2.3 使用线程池2.4 配置内存大小III 预备知识

【面试经典150 | 动态规划】不同路径 II

文章目录 写在前面Tag题目1方法一&#xff1a;动态规划方法二&#xff1a;空间优化 题目2方法一&#xff1a;动态规划空间优化 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主…

启动mysql

删除C:\Program Files (x86)\MySQL\MySQL Server 5.7这个路径下的data文件夹&#xff0c;这个很难删除&#xff0c;因为一开机&#xff0c;mysql的某些服务就启动了&#xff0c;每次重新启动mysql之前&#xff0c;都要删除这个文件夹 因为这个文件夹在后端执行一些我们看不到的…

2024.3.31学习记录————树莓派4B学习一:环境配置

2024.3.31学习记录————树莓派4B学习一&#xff1a;环境配置 准备跳槽&#xff0c;重新选择方向以嵌入式为目标进行学习&#xff0c;因没有硬件基础&#xff0c;先选择了树莓派&#xff0c;准备通过树莓派开发板对驱动初步熟悉后再开始STM32和51单片机开发。 硬件配置 硬…

[技术闲聊]我对电路设计的理解(五)-如何获取datasheet

一、序言 电路设计的前提是明确设计规格&#xff0c;并转化为具体功能&#xff0c;依据功能确定芯片选型范围。 元器件选型的前提是了解每一类芯片的功能&#xff0c;界定该芯片是否合适。 了解的前提是有datasheet文件可以看。 二、获取datasheet的途径有三类 一类&#x…

MySQL-逻辑架构:逻辑架构分析、SQL执行流程、数据库缓冲池

逻辑架构 1. 逻辑架构剖析 1.1 第1层&#xff1a;连接层 系统&#xff08;客户端&#xff09;访问MySQL服务器前&#xff0c;做的第一件事就是建立TCP连接。 经过三次握手建立连接成功后&#xff0c;MySQL服务器对TCP传输过来的账号密码做身份认证、权限获取。 用户名或密码…

numpy 向量的运算法则公式

import numpy as np #构建向量数组 anp.array([-1,2]) bnp.array([3,-1]) #加法 a_bab #数乘 a2a*2 b3b*(-3) #减法 b_aa-b #print(a_b,a2,b3,b_a,a,b)print(-13,2-1,a_b)print(-1-3,2-(-1),b_a)print((-1*2),(2*2),a2)print((3*(-3)),(-1*(-3)),b3) 这段代码主要是使用NumPy库…

C语言------冒泡法排序

一.前情提要 1.介绍 冒泡法排序法&#xff1a; 1)冒泡排序&#xff08;Bubble Sort&#xff09;是一种简单的排序算法&#xff0c;它重复地遍历要排序的列表&#xff0c;一次比较相邻的两个元素&#xff0c;并且如果它们的顺序错误就将它们交换过来。重复这个过程直到没有需…

DeepWalk论文翻译

DeepWalk论文翻译 DeepWalk: Online Learning of Social Representations DeepWalk&#xff1a;社会表征的在线学习 ABSTRACT 我们提出了 DeepWalk&#xff0c;一种学习网络中顶点潜在表示的新方法。这些潜在表示在连续向量空间中对社会关系进行编码&#xff0c;很容易被统…

VRRP虚拟路由器冗余协议

vrrp是为了解决单点故障问题 将几台路由器联合成一台虚拟的路由器&#xff0c;保证通信的可靠性 协议小说&#xff1a; 协议不是在固定的哪一个层&#xff0c;是基于哪一层工作&#xff0c;比如说ospf是基于三层工作的 VRRP是基于三层工作的&#xff0c;就在前面会封装一个ip…