宏允许开发者在编译时生成代码,从而减少重复工作并提升灵活性。
Rust 的宏分为两类:
- 声明式宏(Declarative Macros):通过
macro_rules!
定义,基于模式匹配和文本替换,简单易用但功能有限。 - 过程宏(Procedural Macros):通过解析和修改
AST
(抽象语法树)生成代码,包括派生宏、属性式宏和函数式宏,更强大但实现复杂。
一、常见的宏
fn main() {
// println! - 输出文本
println!("Processing data...");
// vec! - 创建动态数组
let numbers = vec![10, 20, 30];
// assert! - 验证条件
let value = 5;
assert!(value > 0, "Value must be positive!");
// format! - 生成格式化字符串
let text = format!("Found {} items", numbers.len());
println!("{}", text);
}
二、声明式宏
基础语法
// 多模式声明式宏
macro_rules! log_items {
// 匹配模式1
($item:expr, $count:expr) => {{
for _ in 0..$count {
println!("Item: {}", $item);
}
}};
// 匹配模式2
($item:expr) => {{
println!("Single item: {}", $item);
}};
}
fn main() {
// 根据参数数量自动选择匹配的模式
log_items!("box", 2);
log_items!("crate");
// 输出:
// Item: box
// Item: box
// Single item: crate
}
说明:
-
$item:expr
匹配一个表达式(如"box"),$count:expr
匹配次数,宏展开为一个 for 循环,直接嵌入代码。 - 注意 specifier (如上的 expr)不能随便写,rust 提供了一系列合法的 specifier,需要根据需求按需选择
可变参数宏
macro_rules! sum_values {
// $($value:expr),* 表示零个或多个表达式,展开时为每个值生成 total += $value。这使得宏能处理任意数量的输入。
($($value:expr),*) => {{
let mut total = 0;
$(
total += $value;
)*
total
}};
}
fn main() {
let result = sum_values!(1, 2, 3, 4);
println!("Sum: {}", result); // 输出: Sum: 10
}
三、过程宏
派生宏为类型自动实现特征
派生宏
#[derive(Debug)]
struct Record {
id: u32,
name: String,
}
fn main() {
let record = Record {
id: 1,
name: String::from("Item A"),
};
println!("{:?}", record); // 输出: Record { id: 1, name: "Item A" }
}
#[derive(Debug)]
在编译时为 Record 追加 Debug 特征的实现。
自定义派生宏代码模板:
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn;
// 自定义派生宏;quote! 生成特征实现代码,#name 插入类型名称
#[proc_macro_derive(Report)]
pub fn report_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();
let name = &ast.ident;
let gen = quote! {
impl Report for #name {
fn report() {
println!("Reporting: {}", stringify!(#name));
}
}
};
gen.into()
}
// 使用示例
#[derive(Report)]
struct Box;
#[derive(Report)]
struct Tray;
fn main() {
Box::report(); // 输出: Reporting: Box
Tray::report(); // 输出: Reporting: Tray
}
属性式宏
属性式宏通过 #[attr] 为代码添加自定义属性,扩展其功能:
#[proc_macro_attribute]
pub fn track_call(attr: TokenStream, item: TokenStream) -> TokenStream {
let item_ast = syn::parse::<syn::ItemFn>(item.clone()).unwrap();
let fn_name = &item_ast.sig.ident;
let gen = quote! {
#item_ast
fn track_wrapper() {
println!("Calling function: {}", stringify!(#fn_name));
// 调用 process_data()
#fn_name();
}
};
gen.into()
}
// 使用示例
#[track_call]
fn process_data() {
println!("Data processed.");
}
fn main() {
track_wrapper(); // 输出: Calling function: process_data \n Data processed.
}
函数式宏
函数式宏通过
name!(...)
调用,像函数但在编译时生成代码,类似println!(...)
#[proc_macro]
pub fn log_event(input: TokenStream) -> TokenStream {
let input_str = input.to_string();
let gen = quote! {
println!("Event logged: {}", #input_str);
};
gen.into()
}
// 使用示例
fn main() {
log_event!("system start");
// 输出: Event logged: system start
}
宏与函数
fn log_once() {
println!("Logged!");
}
macro_rules! log_once {
() => {
println!("Logged!");
};
}
fn main() {
log_once(); // 函数调用
log_once!(); // 宏展开
}