宏是一种可以生成代码的代码,这种形式被称为“元编程”(metaprogramming)。我们已经使用过 Rust 中的多个宏,例如 println!
, vec!
, #[derive]
等。这些宏都会在编译预处理时展开来生成代码。
Rust 中的宏有多个种类,最常用的是“声明型宏”(declarative macros),此外还有三种“过程型宏”(procedural macros):
- 通过
#[derive]
为结构体或者枚举类型添加代码 - 属性
- 函数
声明型宏
声明型宏会比较传入的表达式是否符合格式要求,如果符合则用传入的表达式替换宏中的 placeholder。
例如,我们仿照 vec!
宏实现一个简单的 my_vec!
宏:
// brings macro into the scope
#[macro_export]
macro_rules! my_vec {
// similar to "match" expression
( $( $x:expr ),* ) => {
// if pattern matches, below code is emitted
{ // this "{}" is needed because the macro will be used in a one-line expression
let mut temp_vec = Vec::new();
$(
temp_vec.push($x);
)*
temp_vec
}
};
}
使用方式和 vec!
一致:
use macros::my_vec;
fn main() {
let v = my_vec![1, 3, 5];
println!("Hello, {:?}!", v);
}
现在来分析代码中的关键点。
-
类似于
match
的结构,根据不同的模式生成不同代码,上面只匹配一种模式 -
( $( $x:expr ),* )
是一种模式。expr
指 Rust 表达式,这里将匹配 Rust 表达式并赋值给$x
变量,,
就是指,
字符,*
表示可以出现任意次或者不出现。基本类似于正则表达式。结果是,当我们调用my_vec![1, 3, 5]
时,$x
匹配到了1
,3
,5
。 -
$(temp_vec.push($x);)*
表明,模式匹配成功多少次,就对$x
调用多少次temp_vec.push($x)
。对于my_vec![1, 3, 5]
,生成的代码是:
{
let mut temp_vec = Vec::new();
temp_vec.push(1);
temp_vec.push(3);
temp_vec.push(5);
temp_vec
}
macro_rules!
有一些 edge case 不好处理,Rust 会在将来用更好的实现代替它。实际上,对于大部分的开发者而言,Rust 鼓励大家去使用 macro 而不是自己编写 macro。
过程型宏
(编写中)
网友评论