学习rust_day18
Macro (宏)
- Macro“宏”,在Rust中是一个“家族”,宏主要分为两大类:
- 声明式宏(Declarative Macros),也就是通过macro_rules!定义的宏。
- 过程宏(Procedural Macros),它又可以细分为三种不同的形式:
- 自定义的[derive]宏,它们用于结构体(struct)或枚举(enum)上,配合derive属性,自动为类型生成一些代码。
- 类似属性的宏(Attribute–like macros),它们可以用作任意代码项上的自定义属性。
- 类似函数的宏(Function–like macros),它们看起来像函数调用,但实际上操作的是传入的代码片段(tokens)。
宏与函数的区别
- 宏是一种用来编写代码的代码,也就是所谓的元编程
- 这些宏最终会扩展成比你手动写的更多的代码
- 宏拥有一些函数无法做到的能力
- 但宏可以接收不定数量的参数
- 宏在编译器解释代码意义之前就会被展开
- 定义一个宏往往比定义一个函数要复杂得多
- 宏必须在使用之前定义好或导入作用域
声明式宏(Declarative Macros)
- 声明式宏让你可以像使用match表达式一样,匹配代码结构并生成新的代码
- 宏匹配的不是运行时的值,而是Rust源代码的结构本身
- 匹配成功后,对应的代码就会被替换到宏调用的位置
- 这一切都发生在编译期间
例:
1 | |
- #[macro_export]表示这个宏在当前crate被引入作用域时也能被访问到
- macro rules!是声明宏的关键字,后面跟着宏的名字(这里是vec),不带感叹号!
- 大括号{}包裹的就是宏的模式匹配和替换规则
($($x:expr),*) => {…}
- $(…)是Rust宏中表示重复模式的语法
- $x:expr匹配任意Rust表达式,并将其命名为$x
- ,表示每个表达式之间必须有逗号分隔
- *表示这个模式可以重复零次或多次
过程宏:通过属性生成代码
- 过程宏更像是函数,它们接收一些代码作为输入,对其进行处理,然后生成一些代码作为输出,而不是通过模式匹配和替换代码的方式来运作
- Rust中有三种主要的过程宏形式:
- 自定义派生(custom derive)
- 类似属性的宏(attribute-like)
- 类似函数的宏(function-like)
- 要定义过程宏,我们必须将它们写在一个单独的crate中,而且这个crate还必须具有特殊的crate类型
- 这是一种目前还存在的技术限制,未来可能会被移除
例:
1 | |
- 定义过程宏的函数接受一个TokenStream类型的参数,返回一个同样类型的结果
- TokenStream是由Rust自带的proc_macro crate提供的,表示一串令牌的序列,
- 换句话说:
- 输入的TokenStream是宏要处理的源代码
- 输出的TokenStream是宏生成的新代码
- 在函数的上方,还需要添加一个属性(attribute),用来表明这是哪一种过程宏
- 一个crate中可以定义多种类型的过程宏。
如何编写一个自定义的derive宏 Custom Derive Macro
- 我们将创建一个名为hello macro的crate
- 它定义了一个trait:HelloMacro
- 该trait有一个关联函数:hello_macro()
- 我们希望用户只需在他们的类型上添加#[derive(HelloMacro)],就能自动获得一个默认实现
- 这个默认实现会打印:Hello,Macro!My name is TypeName!
例:
hello_macro/src/lib.rs:
1 | |
hello_macro/hello_macro_derive/src/lib.rs:
1 | |
hello_macro/hello_macro_derive/Cargo.toml:
1 | |
pancakes/src/main.rs:
1 | |
pancakes/Cargo.toml:
1 | |
属性宏Attribute–like Macros
- 属性宏和自定义derive宏(custom derive macros)类似
- 不同的是,属性宏允许你创建新的属性(attribute),这使得它更加灵活
1 | |
1 | |
- 属性宏和自定义derive宏的核心机制一样:你需要创建一个专门的宏crate,然后写一个函数,接收TokenStream,生成你希望的代码
函数宏Function–like Macros
- 函数宏看起来像是一个函数调用
1 | |
1 | |
- 跟derive宏很像,函数宏也是接收一个TokenStream,生成代码返回
- macro_.rules!宏不同的是:macro_.rules!使用的是匹配规则语法;而函数宏使用的是Rust代码来处理TokenStream,因此处理逻辑可以更复杂更灵活
学习rust_day18
https://zlsf-zl.github.io/2026/03/28/学习rust-day18/