文章目录
-
-
- 一、元编程概念
- [二、声明式宏(Declarative Macros)](#二、声明式宏(Declarative Macros))
- [三、过程宏(Procedural Macros)](#三、过程宏(Procedural Macros))
- 四、常见内置宏及使用示例
- 五、宏优势(对比普通函数)
- [本章小结 + 进阶练习](#本章小结 + 进阶练习)
-
摘要 :Rust 元编程(Metaprogramming)通过宏(Macros)在编译期生成代码,极大减少重复、实现 DSL、扩展语言能力。宏分为声明式宏(Declarative Macros) (macro_rules!,模式匹配展开)和过程宏(Procedural Macros) (三种:函数式、派生#[derive]、属性#[proc_macro_attribute])。内置宏包括format!、dbg!、assert!、vec!、todo!、#[derive(Debug)]、#[cfg]等。宏优势:生成代码(而非执行)、支持变长参数、类型特定展开、零运行时开销。掌握macro_rules!模式 + proc-macro crate + TokenStream,你就能写出println!级别的自定义宏、自动派生 trait、条件编译,实现"少写代码、多干实事"的极致生产力。(168 字)
一、元编程概念
专业名词释义:
- 元编程(Metaprogramming):编写能生成代码的代码,减少手写量、扩展语言本身。
- 宏(Macros):Rust 元编程核心,在编译期展开为普通 Rust 代码。
Rust 宏分类:
- 声明式宏(Declarative Macros) :
macro_rules!定义,模式匹配 + 替换。 - 过程宏(Procedural Macros) :Rust 函数,接收
TokenStream返回新代码。- 函数式宏(
fn_macro!) - 派生宏(
#[derive]) - 属性宏(
#[attr])
- 函数式宏(
注意事项与最佳实践:
- 宏是编译期生成代码,无运行时开销。
- 深度提示 :Rust 不支持运行时完整反射(仅
Any),元编程全靠宏。 - 最佳实践 :日常用声明式宏,复杂逻辑用过程宏(需
proc-macrocrate)。
二、声明式宏(Declarative Macros)
用法示例(可变参数求和):
rust
macro_rules! sum_of_numbers {
($($x:expr),*) => {
{
let mut result = 0.0;
$(
result += $x as f64;
)*
result as i64
}
};
}
fn main() {
let res = sum_of_numbers!(1, 4.2, 5); // 展开后为 10
}
展开形式(编译器实际生成):
rust
let res = {
let mut result = 0.0;
result += 1 as f64;
result += 4.2 as f64;
result += 5 as f64;
result as i64
};
专业名词释义:
($($x:expr),*):重复模式(*表示零或多次,,分隔)。$x:expr:捕获表达式。
注意事项与最佳实践:
- 模式必须覆盖所有情况,否则编译错误。
- 深度提示 :
macro_rules!是卫生宏(hygiene),不会污染调用者作用域。 - 最佳实践 :用
$(...)*处理变长参数,解决 Rust 无原生可变参问题。
三、过程宏(Procedural Macros)
三种形式:
- 函数式宏 :
fn_macro_custom_syntax! { ... } - 派生宏 :
#[derive(Describe)] - 属性宏 :
#[proc_macro_attribute]
示例(属性宏):
rust
#[proc_macro_attribute]
pub fn show_streams(attr: TokenStream, item: TokenStream) -> TokenStream {
println!("attr: \"{}\"", attr.to_string());
println!("item: \"{}\"", item.to_string());
item
}
派生宏示例 (自动实现describe()):
rust
#[derive(Describe)]
struct MyStruct { ... }
注意事项与最佳实践:
- 过程宏必须单独 crate(
proc-macro = true)。 - 深度提示 :过程宏操作
TokenStream(语法树),功能远超声明式宏。 - 最佳实践 :
syn+quote组合是过程宏标配(解析 + 生成)。
四、常见内置宏及使用示例
核心内置宏列表(附释义与用法):
-
format!/println!:格式化字符串(Display/Debug)。rustlet info = format!("{} is {} years old.", name, age); println!("{:?}", vec![1,2,3]); -
dbg!:调试神器(打印 + 返回值)。rustlet y = dbg!(x * 2) - 1; // 输出: [src/main.rs:4] x * 2 = 10 -
assert!/assert_eq!/assert_ne!:运行时断言。 -
vec!:创建 Vec。 -
todo!/unimplemented!:占位(编译通过,运行 panic)。 -
cfg!/#[cfg(target_os = "linux")]:条件编译。 -
include_str!/include_bytes!:文件嵌入。 -
panic!/unreachable!:显式终止。
属性宏(最常用派生):
#[derive(Debug, Clone, PartialEq, Hash)]#[cfg],#[test],#[inline],#[repr(C)],#[allow(dead_code)]
注意事项与最佳实践:
todo!只在测试/占位时用,生产代码必须替换。- 深度提示 :
dbg!在 release 仍生效(生产慎用)。 - 最佳实践 :
#[derive(...)]自动实现 trait,零代码重复。
五、宏优势(对比普通函数)
宏 vs 函数:
- 生成代码 vs 执行代码:宏在编译期展开,函数运行时调用。
- 消除重复 :
println!支持任意参数。 - 变长参数:Rust 无原生可变参,宏完美解决。
- 类型特定代码:根据输入类型生成不同实现(声明宏示例)。
- 零运行时开销:宏展开后就是普通代码。
注意事项与最佳实践:
- 宏滥用会导致代码难以阅读("宏地狱")。
- 深度提示 :过程宏可实现 DSL(领域特定语言),如
rocket、serde。 - 最佳实践:简单重复用声明式宏,复杂 trait 派生用过程宏。
本章小结 + 进阶练习
学完本章你应该能做到:
- 理解元编程本质 + 宏分类
- 熟练写
macro_rules!+ 使用内置宏 - 知道过程宏三种形式及
#[derive]作用
进阶练习 (建议立刻敲代码 + cargo expand 查看展开):
- 实现
macro_rules!版vec!(支持任意类型)。 - 写
#[derive(Describe)]过程宏(自动输出字段名)。 - 用
#[cfg]实现平台特定代码(Linux/Windows)。 - 自定义
debug!宏(带文件名行号)。 - 用属性宏实现
#[timed](自动计时函数)。 - 实现
print_hex!(支持{:x}格式)。
元编程 = Rust 生产力核武器 。掌握宏,你就能写出serde、tokio级别的库,真正做到"一次定义,处处使用"!
(完)