第23章:宏系统
第23章:宏系统
23.1 宏概述
| 类型 | 关键字 | 说明 | 用途 |
|---|
| 声明宏 | macro_rules! | 模式匹配的宏 | 代码复用 |
| derive 宏 | #[derive(...)] | 自动实现 trait | 派生实现 |
| 属性宏 | #[my_attr] | 自定义属性 | 代码生成 |
| 函数式宏 | my_macro!(...) | 过程宏 | DSL、代码生成 |
23.2 声明宏(macro_rules!)
基本语法
// 定义一个简单的宏
macro_rules! say_hello {
() => {
println!("Hello, macro!");
};
}
// 带参数的宏
macro_rules! create_function {
($func_name:ident) => {
fn $func_name() {
println!("调用了函数: {}", stringify!($func_name));
}
};
}
fn main() {
say_hello!();
create_function!(foo);
create_function!(bar);
foo();
bar();
}
匹配器模式
macro_rules! calculate {
// 匹配 add(a, b)
(add $a:expr, $b:expr) => {
$a + $b
};
// 匹配 mul(a, b)
(mul $a:expr, $b:expr) => {
$a * $b
};
// 匹配多个表达式
(sum $($x:expr),+ $(,)?) => {
{
let mut total = 0;
$(total += $x;)+
total
}
};
}
fn main() {
println!("add: {}", calculate!(add 2, 3)); // 5
println!("mul: {}", calculate!(mul 4, 5)); // 20
println!("sum: {}", calculate!(sum 1, 2, 3, 4)); // 10
}
常用片段说明符
| 说明符 | 匹配 | 示例 |
|---|
$x:expr | 表达式 | 1 + 2, vec![1] |
$x:ty | 类型 | i32, Vec<String> |
$x:ident | 标识符 | foo, my_var |
$x:pat | 模式 | _, Some(x) |
$x:stmt | 语句 | let x = 1; |
$x:block | 代码块 | { ... } |
$x:item | 项 | fn, struct, impl |
$x:literal | 字面量 | 42, "hello" |
$x:tt | 单个 token tree | 任意 |
$x:meta | 属性内容 | derive(Debug) |
实用宏示例
// HashMap 字面量宏
macro_rules! map {
($($key:expr => $value:expr),* $(,)?) => {
{
let mut map = std::collections::HashMap::new();
$(map.insert($key, $value);)*
map
}
};
}
// 测量执行时间的宏
macro_rules! time_it {
($label:expr, $block:block) => {
{
let start = std::time::Instant::now();
let result = $block;
let elapsed = start.elapsed();
println!("[{}] 耗时: {:?}", $label, elapsed);
result
}
};
}
// 重复调用宏
macro_rules! repeat {
($n:expr, $body:expr) => {
for _ in 0..$n {
$body;
}
};
}
fn main() {
let scores = map! {
"Alice" => 95,
"Bob" => 87,
"Charlie" => 92,
};
println!("{:?}", scores);
let sum = time_it!("求和", {
(1..=1_000_000).sum::<i64>()
});
println!("结果: {}", sum);
repeat!(3, println!("重复执行"));
}
递归宏
macro_rules! count {
() => (0usize);
($head:tt $($tail:tt)*) => (1usize + count!($($tail)*));
}
macro_rules! vec_of_strings {
($($s:expr),* $(,)?) => {
vec![$($s.to_string()),*]
};
}
fn main() {
println!("count: {}", count!(a b c d e)); // 5
let names = vec_of_strings!["Alice", "Bob", "Charlie"];
println!("{:?}", names);
}
23.3 过程宏(Procedural Macros)
过程宏需要单独的 crate:
# Cargo.toml
[lib]
proc-macro = true
[dependencies]
syn = "2"
quote = "1"
proc-macro2 = "1"
derive 宏
// 在 proc-macro crate 中
use proc_macro::TokenStream;
use quote::quote;
use syn::DeriveInput;
#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).unwrap();
let name = &ast.ident;
let gen = quote! {
impl HelloMacro for #name {
fn hello_macro() {
println!("Hello, I'm {}!", stringify!(#name));
}
}
};
gen.into()
}
// 使用 derive 宏
trait HelloMacro {
fn hello_macro();
}
#[derive(HelloMacro)]
struct Pancakes;
fn main() {
Pancakes::hello_macro(); // Hello, I'm Pancakes!
}
属性宏
// proc-macro crate
#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
let input: syn::ItemFn = syn::parse(item).unwrap();
let name = &input.sig.ident;
let attr_str = attr.to_string();
let gen = quote! {
pub fn #name() {
println!("路由: {}", #attr_str);
#input
}
};
gen.into()
}
函数式过程宏
#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
let query = input.to_string();
let gen = quote! {
{
println!("SQL: {}", #query);
// 在实际项目中,这里会编译时验证 SQL 语法
#query.to_string()
}
};
gen.into()
}
23.4 常用 derive 宏
| 宏 | 来源 | 说明 |
|---|
Debug | std | 调试格式化 |
Clone | std | 深拷贝 |
Copy | std | 位拷贝 |
PartialEq | std | 相等比较 |
Eq | std | 完全相等 |
Hash | std | 哈希 |
Default | std | 默认值 |
Serialize | serde | 序列化 |
Deserialize | serde | 反序列化 |
Parser | clap | 命令行解析 |
Error | thiserror | 错误类型 |
23.5 宏调试
cargo expand
# 安装
cargo install cargo-expand
# 展开宏
cargo expand
cargo expand my_module
编译时调试
macro_rules! debug_macro {
($($tt:tt)*) => {
// 打印宏接收到的 token
compile_error!(concat!("tokens: ", stringify!($($tt)*)));
};
}
// 使用 trace_macros!(nightly)
// #![feature(trace_macros)]
// trace_macros!(true);
23.6 业务场景示例
Builder 模式的宏
macro_rules! builder {
(
$(#[$attr:meta])*
$vis:vis struct $name:ident {
$(
$field_vis:vis $field:ident : $field_type:ty
),* $(,)?
}
) => {
$(#[$attr])*
$vis struct $name {
$(pub $field: $field_type,)*
}
paste::paste! {
pub struct [<$name Builder>] {
$($field: Option<$field_type>,)*
}
impl [<$name Builder>] {
pub fn new() -> Self {
Self {
$($field: None,)*
}
}
$(
pub fn $field(mut self, value: $field_type) -> Self {
self.$field = Some(value);
self
}
)*
pub fn build(self) -> Result<$name, String> {
Ok($name {
$($field: self.$field.ok_or(
concat!(stringify!($field), " 未设置")
)?,)*
})
}
}
}
impl $name {
pub fn builder() -> [<$name Builder>] {
[<$name Builder>]::new()
}
}
};
}
fn main() {
// 使用 builder 宏
// let user = User::builder()
// .name("Alice".to_string())
// .email("[email protected]".to_string())
// .build()
// .unwrap();
println!("Builder 宏示例");
}
日志宏
macro_rules! log {
(info, $($arg:tt)*) => {
println!("[INFO {}:{}] {}", file!(), line!(), format_args!($($arg)*));
};
(warn, $($arg:tt)*) => {
eprintln!("[WARN {}:{}] {}", file!(), line!(), format_args!($($arg)*));
};
(error, $($arg:tt)*) => {
eprintln!("[ERROR {}:{}] {}", file!(), line!(), format_args!($($arg)*));
};
}
fn main() {
log!(info, "服务器启动在端口 {}", 8080);
log!(warn, "连接数接近上限: {}/{}", 95, 100);
log!(error, "数据库连接失败: {}", "超时");
}
23.7 本章小结
| 要点 | 说明 |
|---|
| 声明宏 | macro_rules!,模式匹配 |
| derive 宏 | 自动实现 trait |
| 属性宏 | 自定义属性处理 |
| 过程宏 | 需要单独 crate,使用 syn/quote |
| cargo expand | 宏展开调试工具 |
扩展阅读
- The Rust Reference - Macros — 宏参考
- The Little Book of Rust Macros — 声明宏详解
- syn 文档 — 过程宏必备库