====== 第十六章 宏 ======
宏(Macro)是Rust中一种强大的元编程工具,允许你编写生成代码的代码。Rust有两种类型的宏:声明宏(declarative macros)和过程宏(procedural macros)。本章将详细介绍这两种宏的工作原理和使用方法。
===== 1. 宏概述 =====
==== 1.1 什么是宏 ====
宏是一种代码生成机制,在编译时展开为实际的Rust代码。与函数不同,宏可以:
* 接收可变数量的参数
* 在编译时生成代码
* 操作Rust的抽象语法树(AST)
==== 1.2 宏与函数的区别 ====
| 特性 | 函数 | 宏 |
| 参数数量 | 固定 | 可变 |
| 执行时机 | 运行时 | 编译时 |
| 类型检查 | 严格 | 模式匹配 |
| 代码生成 | 否 | 是 |
// 函数 - 固定参数
fn add(a: i32, b: i32) -> i32 {
a + b
}
// 宏 - 可变参数
println!("{} {}", 1, 2);
println!("{} {} {}", 1, 2, 3);
vec![1, 2, 3, 4, 5]; // 任意数量的元素
===== 2. 声明宏(macro_rules!) =====
声明宏使用 macro_rules! 定义,是最常见的宏类型。
==== 2.1 简单的宏定义 ====
// 定义一个简单的宏
macro_rules! say_hello {
() => {
println!("Hello!");
};
}
fn main() {
say_hello!(); // 输出: Hello!
}
==== 2.2 带参数的宏 ====
macro_rules! print_twice {
($x:expr) => {
println!("{}", $x);
println!("{}", $x);
};
}
fn main() {
print_twice!("Rust");
print_twice!(42);
}
==== 2.3 多个匹配模式 ====
macro_rules! greet {
// 无参数
() => {
println!("Hello!");
};
// 一个参数
($name:expr) => {
println!("Hello, {}!", $name);
};
// 两个参数
($greeting:expr, $name:expr) => {
println!("{}, {}!", $greeting, $name);
};
}
fn main() {
greet!();
greet!("World");
greet!("Good morning", "Alice");
}
==== 2.4 重复模式 ====
使用 $(...)* 或 $(...)+ 匹配重复的模式。
macro_rules! vector {
($($x:expr),*) => {
{
let mut temp_vec = Vec::new();
$(
temp_vec.push($x);
)*
temp_vec
}
};
}
fn main() {
let v = vector![1, 2, 3, 4, 5];
println!("{:?}", v);
let empty: Vec = vector![];
println!("{:?}", empty);
}
==== 2.5 类似vec!的宏 ====
macro_rules! my_vec {
// vec![elem; len]
($elem:expr; $n:expr) => {
std::vec::from_elem($elem, $n)
};
// vec![a, b, c]
($($x:expr),+ $(,)?) => {
<[_]>::into_vec(Box::new([$($x),+]))
};
}
fn main() {
let v1 = my_vec![0; 5];
println!("{:?}", v1); // [0, 0, 0, 0, 0]
let v2 = my_vec![1, 2, 3];
println!("{:?}", v2); // [1, 2, 3]
}
===== 3. 捕获类型 =====
==== 3.1 常见的捕获类型 ====
| 捕获类型 | 描述 | 示例 |
| expr | 表达式 | 1 + 2, "hello" |
| stmt | 语句(不含分号) | let x = 5 |
| block | 代码块 | { let x = 1; x + 2 } |
| pat | 模式 | Some(x), (a, b) |
| ty | 类型 | i32, Vec |
| ident | 标识符 | foo, Bar |
| path | 路径 | std::mem::drop |
| tt | token树 | 任意token序列 |
| item | 项 | fn, struct, use等 |
==== 3.2 使用不同捕获类型 ====
macro_rules! create_function {
// ident捕获函数名
($func_name:ident) => {
fn $func_name() {
println!("你调用了 {:?}", stringify!($func_name));
}
};
}
create_function!(foo);
create_function!(bar);
fn main() {
foo();
bar();
}
==== 3.3 类型捕获 ====
macro_rules! impl_display {
($t:ty) => {
impl std::fmt::Display for $t {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
};
}
struct Wrapper(i32);
impl_display!(Wrapper);
fn main() {
let w = Wrapper(42);
println!("{}", w); // 42
}
===== 4. 高级宏技巧 =====
==== 4.1 重复分隔符 ====
macro_rules! sum {
// 最后一个逗号是可选的
($($x:expr),+ $(,)?) => {
0 $(+ $x)*
};
}
fn main() {
println!("{}", sum!(1, 2, 3));
println!("{}", sum!(1, 2, 3,)); // 末尾逗号也可以
}
==== 4.2 重复索引 ====
macro_rules! tuple_to_array {
($($x:expr),+ $(,)?) => {
[$(($x, stringify!($x))),+]
};
}
fn main() {
let arr = tuple_to_array!(1, 2, 3);
for (val, name) in arr {
println!("{} 的名字是 {}", val, name);
}
}
==== 4.3 嵌套重复 ====
macro_rules! matrix {
($($($x:expr),+);+ $(;)?) => {
vec![$(vec![$($x),+]),+]
};
}
fn main() {
let m = matrix!(
1, 2, 3;
4, 5, 6;
7, 8, 9
);
println!("{:?}", m);
}
==== 4.4 递归宏 ====
macro_rules! calculate {
// 基本情况
(eval $e:expr) => {{
let val: i32 = $e;
println!("{} = {}", stringify!($e), val);
val
}};
// 递归展开
(eval $e:expr $(, $rest:expr)+) => {{
calculate!(eval $e);
calculate!(eval $($rest),+)
}};
}
fn main() {
calculate!(eval 1 + 2, 3 * 4, 10 / 2);
}
===== 5. 卫生宏(Hygienic Macros) =====
Rust的宏是卫生的,意味着宏内部定义的变量不会与外部变量冲突。
macro_rules! define_x {
() => {
let x = 42;
println!("宏内部: x = {}", x);
};
}
fn main() {
let x = 10;
define_x!();
println!("外部: x = {}", x); // 仍然是10,没有被宏改变
}
如果需要访问外部变量,必须作为参数传入:
macro_rules! set_value {
($var:ident, $val:expr) => {
$var = $val;
};
}
fn main() {
let mut x = 10;
set_value!(x, 42);
println!("x = {}", x); // 42
}
===== 6. 过程宏(Procedural Macros) =====
过程宏更强大也更复杂,它们直接操作Rust的抽象语法树(AST)。
==== 6.1 过程宏类型 ====
* **自定义派生(Derive macro)**:为struct和enum添加derive属性
* **属性宏(Attribute macro)**:定义新的属性
* **函数式宏(Function-like macro)**:看起来像函数调用的宏
==== 6.2 创建过程宏项目 ====
过程宏必须定义在单独的crate中:
cargo new my_derive --lib
编辑 Cargo.toml:
[package]
name = "my_derive"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "2.0", features = ["full"] }
==== 6.3 自定义Derive宏 ====
// my_derive/src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
use syn;
#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
// 解析输入为DeriveInput
let ast = syn::parse(input).unwrap();
// 构建trait实现
impl_hello_macro(&ast)
}
fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let gen = quote! {
impl HelloMacro for #name {
fn hello_macro() {
println!("Hello, Macro! 我是 {}", stringify!(#name));
}
}
};
gen.into()
}
使用:
// 主项目
use my_derive::HelloMacro;
trait HelloMacro {
fn hello_macro();
}
#[derive(HelloMacro)]
struct Pancakes;
fn main() {
Pancakes::hello_macro(); // Hello, Macro! 我是 Pancakes
}
==== 6.4 带属性的Derive宏 ====
// my_derive/src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
use syn::{self, parse::Parser};
#[proc_macro_derive(Builder, attributes(builder))]
pub fn builder_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
impl_builder(&ast)
}
fn impl_builder(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let builder_name = quote::format_ident!("{}Builder", name);
let gen = quote! {
pub struct #builder_name;
impl #name {
pub fn builder() -> #builder_name {
#builder_name
}
}
};
gen.into()
}
==== 6.5 属性宏 ====
属性宏用于定义自定义属性,可以应用于函数、结构体等。
// my_derive/src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
// attr 是属性参数,如 route("GET", "/")
// item 是被装饰的项
let method = attr.to_string();
let input = syn::parse_macro_input!(item as syn::ItemFn);
let name = &input.sig.ident;
let body = &input.block;
let expanded = quote! {
#[allow(dead_code)]
fn #name() {
println!("路由方法: {}", #method);
#body
}
};
expanded.into()
}
使用:
use my_derive::route;
#[route("GET", "/")]
fn index() {
println!("首页");
}
fn main() {
index();
}
==== 6.6 函数式过程宏 ====
函数式过程宏类似于 macro_rules!,但使用Rust代码实现。
// my_derive/src/lib.rs
use proc_macro::TokenStream;
use quote::quote;
use syn;
#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
// 解析SQL语句
let sql = input.to_string();
let expanded = quote! {
println!("执行SQL: {}", #sql);
};
expanded.into()
}
使用:
use my_derive::sql;
fn main() {
sql!(SELECT * FROM users WHERE id = 1);
}
===== 7. 高级过程宏技巧 =====
==== 7.1 解析结构体字段 ====
use proc_macro::TokenStream;
use quote::quote;
use syn::{self, Data, Fields};
#[proc_macro_derive(Getters)]
pub fn getters_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
impl_getters(&ast)
}
fn impl_getters(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let fields = match &ast.data {
Data::Struct(data) => match &data.fields {
Fields::Named(fields) => &fields.named,
_ => panic!("只支持命名字段"),
},
_ => panic!("只支持结构体"),
};
let getters = fields.iter().map(|f| {
let field_name = f.ident.as_ref().unwrap();
let field_ty = &f.ty;
let getter_name = quote::format_ident!("get_{}", field_name);
quote! {
pub fn #getter_name(&self) -> field_ty {
&self.#field_name
}
}
});
let gen = quote! {
impl #name {
#(#getters)*
}
};
gen.into()
}
==== 7.2 错误处理 ====
use proc_macro::TokenStream;
use quote::quote;
use syn::{self, spanned::Spanned};
#[proc_macro_derive(MyTrait)]
pub fn my_trait_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
match impl_my_trait(&ast) {
Ok(tokens) => tokens,
Err(e) => e.to_compile_error().into(),
}
}
fn impl_my_trait(ast: &syn::DeriveInput) -> Result {
// 检查是否实现了Clone
let has_clone = ast.attrs.iter().any(|attr| {
attr.path().is_ident("derive") &&
attr.parse_args::().map(|i| i == "Clone").unwrap_or(false)
});
if !has_clone {
return Err(syn::Error::new(
ast.span(),
"MyTrait需要实现Clone"
));
}
let name = &ast.ident;
Ok(quote! {
impl MyTrait for #name {}
}.into())
}
===== 8. 编译时计算 =====
==== 8.1 const fn 与宏 ====
// const fn在编译时执行
const fn fibonacci(n: u64) -> u64 {
match n {
0 => 0,
1 => 1,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
fn main() {
// 在编译时计算
const FIB_10: u64 = fibonacci(10);
println!("斐波那契数列第10项: {}", FIB_10);
}
==== 8.2 include!宏 ====
// 包含其他文件的内容
include!("some_generated_code.rs");
// 包含文件内容为字符串
const CONFIG: &str = include_str!("config.toml");
// 包含文件内容为字节数组
const BINARY: &[u8] = include_bytes!("data.bin");
===== 9. 条件编译 =====
==== 9.1 cfg属性 ====
// 只在测试时编译
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
// 只在特定操作系统编译
#[cfg(target_os = "linux")]
fn linux_only() {
println!("Linux特定代码");
}
// 多条件
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn linux_x86_64_only() {}
// 任条件
#[cfg(any(target_os = "linux", target_os = "macos"))]
fn unix_like() {}
// 排除条件
#[cfg(not(target_os = "windows"))]
fn not_windows() {}
==== 9.2 cfg宏 ====
fn main() {
if cfg!(target_os = "linux") {
println!("运行在Linux上");
} else if cfg!(target_os = "windows") {
println!("运行在Windows上");
} else {
println!("运行在其他系统上");
}
}
===== 10. 编译时断言 =====
==== 10.1 const_assert ====
// 使用static_assertions crate
use static_assertions::const_assert;
const_assert!(std::mem::size_of::() == 8); // 64位系统
fn main() {
println!("编译时断言通过!");
}
==== 10.2 编译时检查 ====
// 使用const panic进行编译时检查
const fn check_size() {
assert!(std::mem::size_of::() <= 128, "类型太大");
}
struct SmallStruct([u8; 64]);
// struct LargeStruct([u8; 256]); // 编译错误
const _: () = check_size::();
fn main() {}
===== 11. 实际应用案例 =====
==== 11.1 实现自定义vec! ====
macro_rules! my_vec {
// vec![value; count]
($val:expr; $count:expr) => {{
let count = $count;
let mut vec = Vec::with_capacity(count);
for _ in 0..count {
vec.push($val);
}
vec
}};
// vec![a, b, c]
($($val:expr),* $(,)?) => {{
let mut vec = Vec::new();
$(vec.push($val);)*
vec
}};
}
fn main() {
let v1 = my_vec![0; 5];
println!("{:?}", v1); // [0, 0, 0, 0, 0]
let v2 = my_vec![1, 2, 3, 4, 5];
println!("{:?}", v2); // [1, 2, 3, 4, 5]
}
==== 11.2 实现HashMap初始化宏 ====
macro_rules! hashmap {
() => {
::std::collections::HashMap::new()
};
($($key:expr => $value:expr),* $(,)?) => {{
let mut map = ::std::collections::HashMap::new();
$(
map.insert($key, $value);
)*
map
}};
}
fn main() {
let map = hashmap! {
"name" => "Alice",
"age" => "30",
};
println!("{:?}", map);
}
==== 11.3 实现类似try!的错误处理宏 ====
macro_rules! try_or_return {
($expr:expr) => {
match $expr {
Ok(val) => val,
Err(e) => {
eprintln!("错误: {}", e);
return;
}
}
};
($expr:expr, $ret:expr) => {
match $expr {
Ok(val) => val,
Err(e) => {
eprintln!("错误: {}", e);
return $ret;
}
}
};
}
fn may_fail() -> Result {
Ok(42)
}
fn process() -> i32 {
let val = try_or_return!(may_fail(), -1);
val * 2
}
fn main() {
println!("结果: {}", process());
}
===== 12. 调试宏 =====
==== 12.1 展开宏查看结果 ====
使用 cargo expand 查看宏展开后的代码:
cargo install cargo-expand
cargo expand
==== 12.2 编译错误定位 ====
// 使用compile_error!产生编译错误
#[cfg(not(feature = "required_feature"))]
compile_error!("必须启用required_feature特性");
fn main() {}
==== 12.3 日志调试宏 ====
macro_rules! debug_var {
($var:ident) => {
eprintln!("[DEBUG] {} = {:?} (在{}:{})",
stringify!($var),
$var,
file!(),
line!()
);
};
}
fn main() {
let x = 42;
debug_var!(x);
}
===== 13. 最佳实践 =====
- **从简单开始**:先用 macro_rules!,必要时再用过程宏
- **文档化**:为宏提供清晰的文档和示例
- **测试边界情况**:宏的边缘行为可能难以预测
- **使用 $crate**:避免路径问题
- **遵循命名约定**:宏名通常使用 snake_case,过程宏使用 PascalCase
// 使用$crate确保路径正确
#[macro_export]
macro_rules! my_macro {
() => {
$crate::internal_function()
};
}
===== 小结 =====
本章深入探讨了Rust的宏系统:
* **声明宏(macro_rules!)**:模式匹配,适合简单的代码生成
* **过程宏**:操作AST,适合复杂的自定义derive
* **Derive宏**:为类型自动实现trait
* **属性宏**:添加自定义属性
* **函数式宏**:强大的代码转换能力
宏是Rust元编程的核心,合理使用可以大大提高代码的复用性和可维护性。虽然学习曲线较陡,但掌握宏的使用将使你能够编写更加优雅和强大的Rust代码。