声明式宏和过程宏的区别
- 声明式宏(Macro by example):
- 定义:通过模式匹配来展开代码。它基于
macro_rules!
关键字定义,通常用于生成重复的代码结构。
- 适用场景:适用于简单的代码生成和替换,比如生成相同结构的代码片段。
- 过程宏(Procedural macro):
- 定义:以函数的方式定义,接受TokenStream作为输入并返回处理后的TokenStream。有三种类型:函数式宏(
#[proc_macro]
)、类属性宏(#[proc_macro_attribute]
)和类推导宏(#[proc_macro_derive]
)。
- 适用场景:适用于更复杂的代码生成和元编程,例如根据结构体定义自动生成一些方法等。
声明式宏定义和使用示例
// 定义声明式宏
macro_rules! print_vec {
($($x:expr),*) => {
{
let v: Vec<&str> = vec![$(stringify!($x)),*];
println!("{:?}", v);
}
};
}
fn main() {
// 使用声明式宏
print_vec!(hello, world);
}
过程宏(函数式宏)定义和使用示例
- 定义一个函数式宏:
- 创建一个新的Crate,比如
my_proc_macro
,在Cargo.toml
中添加如下内容:
[lib]
proc-macro = true
use proc_macro::TokenStream;
#[proc_macro]
pub fn my_macro(input: TokenStream) -> TokenStream {
input.into_iter().filter(|token| token.to_string()!= " ").collect()
}
- 使用函数式宏:
- 在另一个Crate中,在
Cargo.toml
中添加依赖:
my_proc_macro = { path = "../my_proc_macro" }
extern crate my_proc_macro;
my_proc_macro!(println!("Hello, World!"));
声明式宏模式匹配规则
- 通配符模式:
$()
:用于匹配零个或多个项。例如在print_vec
宏中($($x:expr),*)
,$x
是一个模式变量,*
表示匹配零个或多个表达式,并且每个表达式用逗号分隔。
- 模式变量:
- 模式变量的语法是
$name:tt
形式,其中name
是变量名,tt
是标记树(token tree)类型。常见的类型有expr
(表达式)、stmt
(语句)、ident
(标识符)等。例如$x:expr
表示匹配一个表达式并将其绑定到$x
变量。
- 重复模式:
- 除了
*
(零个或多个),还可以用+
(一个或多个)和?
(零个或一个)。例如($($x:expr),+)
表示匹配一个或多个用逗号分隔的表达式。