rust宏(macro)详解

前言

rust 学习曲线非常陡峭,但是基本语法也还算挺好理解,自动内存管理有点类似智能指针,基本看一下语法入门就可以大概理解,但是唯独宏很难理解,语法非常晦涩。但是功能非常强大。声明宏类似于c语言的宏处理,但是功能更强大。过程宏则类似于Android的注解编程,自定义AbstractProcessor,但是实现更优雅。
下面记录一下宏处理的一些特点

正文

目前主流的文章都是翻译自官方文档,或者取部分Rust语言圣经,关键的部分特性就确实,只有rust宏详解中非常详细的介绍,这里简要记录一下特点

声明宏

声明宏主要是替代,主要是通过简单的模式匹配,然后进行操作,这貌似非常容易处理面向对象的工厂模式,或者解决方法多参数操作,比如

macro_rules! vec {() => ($crate::__rust_force_expr!($crate::vec::Vec::new()));($elem:expr; $n:expr) => ($crate::__rust_force_expr!($crate::vec::from_elem($elem, $n)));($($x:expr),+ $(,)?) => ($crate::__rust_force_expr!(<[_]>::into_vec(// This rustc_box is not required, but it produces a dramatic improvement in compile// time when constructing arrays with many elements.#[rustc_box]$crate::boxed::Box::new([$($x),+]))));
}

这就是根据不同的匹配模式,=>的前半部分,替换成后半部。比如无参数的vec。因为这是系统接口,这里不在详细介绍操作,只介绍匹配,

  1. ()是无参构造函数。
  2. ($elem:expr; $n:expr)这是匹配模式类似vec![1;5],这是创造一个size是5的,值是1的vec。
  3. 这个是匹配vec![1, 2, 3],这是构造一个内容是1、2、3的vec

第二个匹配模式中,$elem是为匹配的内容命名,方便后面使用,expr(一个表达式 (expression))指明匹配的元素,;就不用解释,就是字面值。$n:expr同样道理。
第二个匹配则稍微复杂一些,这里则用的是循环模式。循环是通过$(....)来指明的,括号内为循环内容,为了方便阅读,则需要有分隔符和循环次数,这里是通过定义分隔符,+定义循环至少一次。$(,)?又是一个循环,循环内容则是。而循环一次则是最多一次。

所有的语句如下:

block:一个块(比如一块语句或者由大括号包围的一个表达式)
expr:一个表达式 (expression)
ident:一个标识符 (identifier),包括关键字 (keywords)
item:一个条目(比如函数、结构体、模块、impl 块)
lifetime:一个生命周期注解(比如 'foo、'static)
literal:一个字面值(比如 “Hello World!”、3.14、‘🦀’)
meta:一个元信息(比如 #[…] 和 #![…] 属性内部的东西)
pat:一个模式 (pattern)
path:一条路径(比如 foo、::std::mem::replace、transmute::<_, int>)
stmt:一条语句 (statement)
tt:单棵标记树
ty:一个类型
vis:一个可能为空的可视标识符(比如 pub、pub(in crate))

循环则如下:

反复捕获的一般形式为 $ ( … ) sep rep。

$ 是字面上的美元符号标记
( … ) 是被反复匹配的模式,由小括号包围。
sep 是可选的分隔标记。它不能是括号或者反复操作符 rep。常用例子有 , 和 ; 。
rep 是必须的重复操作符。当前可以是:

  1. ?:表示最多一次重复,所以此时不能前跟分隔标记。
  2. *:表示零次或多次重复。
  3. +:表示一次或多次重复。

过程宏

分为三类

  • 派生宏(Derive macro):用于结构体(struct)、枚举(enum)、联合(union)类型,可为其实现函数或特征(Trait)。
  • 属性宏(Attribute macro):用在结构体、字段、函数等地方,为其指定属性等功能。如标准库中的#[inline]、#[derive(…)]等都是属性宏。
  • 函数式宏(Function-like macro):用法与普通的规则宏类似,但功能更加强大,可实现任意语法树层面的转换功能。

声明宏需要解析传入的参数,进行匹配,而过程宏则需要自己解析传入的内容,然后进行补充,生成代码。这里需要解析TokenStream,举个例子,就是用宏为一个结构体实现构建者模式。

#[derive(Builder)]
struct Command {// ...
}

最麻烦的是如何实现Builder

#[derive(Builder)]
struct Command {input_paht: String,// ...
}pub fn derive_builder(input: TokenStream) -> TokenStream {let input = parse_macro_input!(input as DeriveInput); // 解析input为 DeriveInput类型let input_ident = input.ident;  // 获取原始类名let ident_builder = format_ident!("{}Builder", input_ident.to_string()); // 拼接builder类名if let Data::Struct(r) = input.data {   // 处理结构体let fields = r.fields;// 结构体属性声明let builder_fields = map_fields(&fields, &mut |(ident, ty)| {quote!(#ident: Option<#ty>,) });// 为builder增加set函数let builder_set_fields = map_fields(&fields, &mut |(ident, ty)| {quote!(pub fn #ident(mut self, value: #ty) -> Self {self.#ident = Some(value);self}) });// 获取builder的属性值let builder_lets = map_fields(&fields, &mut |(ident, _)| {quote!(let #ident = self.#ident.ok_or(format!("field {:?}  not set yet", stringify!(#ident),))?;)});// 初始化时的默认值let builder_fields_values = map_fields(&fields, &mut |(ident, _)| {quote!(#ident,)});quote!(impl #input_ident {pub fn builder() -> #ident_builder {#ident_builder::default()}}#[derive(Default)]pub struct #ident_builder {#builder_fields}impl #ident_builder {#builder_set_fieldspub fn build(self) -> Result<#input_ident, String> {#builder_letsOk(#input_ident{ #builder_fields_values })}}).into()} else {// 不支持非struct类型quote!().into()}
}fn map_fields<F>(fields: &Fields, mapper:&mut F) -> TokenStream2
whereF: FnMut((&Option<proc_macro2::Ident> ,  &Type)) -> TokenStream2,
{let fs = fields.iter().map(|field| mapper((&field.ident ,&field.ty)) );let stream2 = TokenStream2::from_iter(fs);stream2
}

这里为Command实现了builder方法如下:

impl Command{pub fn builder() -> CommandBuilder{CommandBuilder::default()}
}pub struct CommandBuilder{input_path: String,
}impl CommandBuilder{pub fn (mut self, value: String) -> Self {self.input_path = Some(value);self}pub fn build(self) -> Result<Command, String> {let input_path= self.input_path.ok_or(format!("field {:?}  not set yet", stringify!(input_path),))?;Ok(Command{ input_path })}}

属性宏则可以传入参数,让控制更自由一些,这里就不在详细介绍
函数式宏则相对比较简单,类似声明宏,但是可以不去匹配规则,更自由,功能更强大。

解析TokenStream需要依赖一些库,这比较复杂,就不在详细介绍。要结合自己代码需求,慢慢理解。

分析工具
cargo.exe  install cargo-expandcargo.exe expand

后记

rust实在是复杂,这里解释一些语法规则,以后遇到问题再补充。

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

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

相关文章

docker-ubuntu中基于keepalived+niginx模拟主从热备完整过程

一、环境准备 &#x1f517;在Ubuntu中安装docker 二、主机 1、环境搭建 1.1 镜像拉取 docker pull ubuntu:16.041.2 创建网桥 docker network create -dbridge --subnet192.168.126.0/24 br11.3 启动容器 docker run -it --name ubuntu-1 --privileged -v /home/vac/l…

为 Compose MultiPlatform 添加 C/C++ 支持(2):在 jvm 平台使用 jni 实现桌面端与 C/C++ 互操作

前言 在上篇文章中我们已经介绍了实现 Compose MultiPlatform 对 C/C 互操作的基本思路。 并且先介绍了在 kotlin native 平台使用 cinterop 实现与 C/C 的互操作。 今天这篇文章将补充在 jvm 平台使用 jni。 在 Compose MultiPlatform 中&#xff0c;使用 jvm 平台的是 An…

Kubernetes实战(十)-升级k8s集群

1 Kubernetes(k8s) 集群升级过程 Kubernetes 使用 kubeadm 工具来管理集群组件的升级。在集群节点层面&#xff0c;升级 Kubernetes(k8s)集群的过程可以分为以下几个步骤&#xff1a; 1&#xff09;检查当前环境和配置是否满足升级要求。 2&#xff09;升级master主节点&…

如何一个例子玩明白GIT

一个例子玩明白GIT GIT的介绍和教程五花八门&#xff0c;但实际需要用的就是建仓、推送、拉取等操作&#xff0c;这儿咱可以通过一个例子熟悉这些操作&#xff0c;一次性搞定GIT的使用方法学习。下面这个例子的内容是内容是建立初始版本库&#xff0c;然后将数据复制到 "远…

轻量封装WebGPU渲染系统示例<45>- 材质组装流水线(MaterialPipeline)灯光、阴影、雾(源码)

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/material/src/voxgpu/sample/MaterialPipelineFog.ts 当前示例运行效果: 此示例基于此渲染系统实现&#xff0c;当前示例TypeScript源码如下&#xff1a; export class MaterialPipelineFog {pr…

数组创建方法

数组的创建 1.let a[] 2.let anew Array(5) 3.let anew Array(1,2,3) 4.let a[1,2,3] 创建数组是空还是有值是以上四种写法。但是如果没给值的变量是undefined&#xff0c;再a[0]找不到这种变量的。所以当找某一个数需要已经是数组内存。不想给值可以给空数组。只要是数组…

MEMS制造的基本工艺介绍——晶圆键合

晶圆键合是一种晶圆级封装技术&#xff0c;用于制造微机电系统 (MEMS)、纳米机电系统 (NEMS)、微电子学和光电子学&#xff0c;确保机械稳定和气密密封。用于 MEMS/NEMS 的晶圆直径范围为 100 毫米至 200 毫米&#xff08;4 英寸至 8 英寸&#xff09;&#xff0c;用于生产微电…

【重点】【环链表入口】142. 环形链表 II

题目 public class Solution {public ListNode detectCycle(ListNode head) {if (head null || head.next null) {return null;}ListNode slow head, fast head;while (fast ! null && fast.next ! null) {slow slow.next;fast fast.next.next;if (slow fast) …

SQL语句---更新数据

介绍 使用sql语句更新数据。 命令 update 表名 set 字段1值1[,字段2值2] [where 条件表达式];[]&#xff08;方括号&#xff09;内的表是表示可选。 例子 将a表id值等于1的数据的名称改为666 update a set name666 where id1;

2023-12-05 Qt学习总结7

点击 <C 语言编程核心突破> 快速C语言入门 Qt学习总结 前言二十 QTcpSocket QTcpServer网络库服务端代码:客户端代码 二十一 QProcess进程类二十二 QThread线程总结 前言 要解决问题: 学习qt最核心知识, 多一个都不学. 二十 QTcpSocket QTcpServer网络库 QTcpSocket和…

持续集成交付CICD:Jenkins流水线实现Nexus制品晋级策略

目录 一、理论 1.开发测试运维环境 二、实验 1.Nexus制品晋级策略 一、理论 1.开发测试运维环境 &#xff08;1&#xff09;环境 1&#xff09;持续集成开发环境&#xff08;DEV: Development Environment&#xff09; 直接通过源代码编译打包&#xff0c;其会跑单元测试…

python 笔记 :trajectory_distance包(如何可以正确使用)【debug篇】

包的地址&#xff1a;maikol-solis/trajectory_distance (github.com) 1 模块介绍 用Cython实现的Python模块&#xff0c;用于计算二维轨迹之间的距离 trajectory_distance包提供了9种轨迹间的距离计算方法&#xff1a; SSPD&#xff08;对称线段路径距离&#xff09;OWD&a…

机器学习算法(9)——集成技术(Bagging——随机森林分类器和回归)

一、说明 在这篇文章&#xff0c;我将向您解释集成技术和著名的集成技术之一&#xff0c;它属于装袋技术&#xff0c;称为随机森林分类器和回归。 集成技术是机器学习技术&#xff0c;它结合多个基本模块和模型来创建最佳预测模型。为了更好地理解这个定义&#xff0c;我们需要…

WLAN配置实验

本文记录了WLAN配置实践的过程&#xff0c;该操作在华为HCIA中属于相对较复杂的实验&#xff0c;记录过程备忘。这里不就WLAN原理解释&#xff0c;仅进行配置实践&#xff0c;可以作为学习原理时候的参考。本文使用华为ENSP进行仿真。实验拓扑图如下&#xff1a; 1.WLAN工作流程…

【electron】外语函数接口 FFI

▒ 目录 ▒ &#x1f6eb; 导读需求开发环境 1️⃣ FFI概念优点注意事项 2️⃣ 【废弃】node-ffi3️⃣ node-ffi-napi安装&#xff08;windows系统下&#xff09;示例&#xff1a;MessageBoxA、NtSuspendProcess 4️⃣ node-win32-api安装示例&#xff1a;查找窗口并设置窗口标…

UE5数据传递-纹理贴图

期待结果&#xff1a; 流程 1. 通过C写入数据到纹理贴图 2. 在材质中通过采样能正确读取写入的数值 踩坑&#xff1a; 1. UE5之后&#xff0c;需要设置采样类型&#xff0c;才能达到上图效果&#xff0c;默认采样类型做了插值计算 FColor中写入 PF_B8G8R8A8 UTexture2D* Conve…

第四题:憧憬(JavaPythonC++实现)【第六届传智杯-新增场次-程序设计挑战赛解题分析详解复盘】

本文仅为【2023传智杯-第二场】第六届传智杯程序设计挑战赛-题目解题分析详解的解题个人笔记,个人解题分析记录。 本文包含:第六届传智杯程序设计挑战赛题目、解题思路分析、解题代码、解题代码详解(Java&Python&C++实现) 文章目录 更新进度记录第四题:憧憬(Java…

AI 绘画 | Stable Diffusion 艺术二维码制作

前言 这篇文章教会你如果用Stable Diffusion WEB UI制作艺术二维码,什么是艺术二维码呢?就是普通二维码和艺术图片融合后的二维码图片,如下图所示。主要原理还是使用controlNet的control_v1p_sd15_qrcode_monster模型和光影模型control_v1p_sd15_brightness。 教程 准备…

【论文阅读笔记】NeRF+Mip-NeRF+Instant-NGP

目录 前言NeRF神经辐射场体渲染连续体渲染体渲染离散化 方法位置编码分层采样体渲染推导公式&#xff08;1&#xff09;到公式&#xff08;2&#xff09;部分代码解读相机变换&#xff08;重要&#xff01;&#xff09; Mip-NerfTo do Instant-NGPTo do 前言 NeRF是NeRF系列的…

DIP——边缘提取与分割

1.使用canny算法进行边缘提取 本实验比较简单&#xff0c;基本思路是对原图像进行一个高斯模糊处理&#xff0c;用于去噪&#xff0c;之后转换为灰度图&#xff0c;直接调用cv库中的canny记性边缘提取。若想直接得到彩色边缘&#xff0c;则通过按位与操作&#xff0c;将原始彩色…