[ Rust笔记 九] 基础-宏

示例

两种方式实现自定义宏:

  1. 通过标准库提供的macro_rules!宏实现

  2. 通过提供编译器扩展来实现

编译器扩展只能在不稳定版本中使用。它的API正在重新设计中,还没有正式定稿,这就是所谓的macro 2.0。

实现这样一个宏定义

1
let counts = hashmap! ['A' => 0, 'C' => 0, 'G' => 0, 'T' => 0];

实现了hashmap! {‘A’ => ‘1’}

1
2
3
4
5
6
macro_rules! hashmap {
($key: expr => $val: expr) => {
// 暂时空着
()
}
}

我们希望这个宏扩展开后的类型是HashMap,而且进行了合理的初始化,那么我们可以使用“语句块”的方式来实现:

1
2
3
4
5
6
7
8
9
macro_rules! hashmap {
($key: expr => $val: expr) => {
{
let mut map = ::std::collections::HashMap::new();
map.insert($key, $val);
map
}
}
}

现在我们希望在宏里面,可以支持重复多个这样的语法元素。我们可以使用+模式和*模式来完成。类似正则表达式的概念,+代表一个或者多个重复,*代表零个或者多个重复。因此,我们需要把需要重复的部分用括号括起来,并加上逗号分隔符:

1
2
3
4
5
6
7
macro_rules! hashmap {
($( $key: expr => $val: expr ), *) => {{
let mut map = ::std::collections::HashMap::new();
map.insert($key, $val);
map
}}
}

最后,我们在语法扩展的部分也使用*符号,将输入部分扩展为多条insert语句。最终的结果如下所示:

1
2
3
4
5
6
7
8
9
10
11
macro_rules! hashmap {
($( $key: expr => $val: expr ), *) => {{
let mut map = ::std::collections::HashMap::new();
$( map.insert($key, $val); )*
map
}}
}
fn main() {
let counts = hashmap! ['A' => 0, 'C' => 0, 'G' => 0, 'T' => 0];
println! ("{:? }", counts);
}

宏1.1

对于一些简单的宏,这种“示例型”​(by example)的方式足够使用了。但是更复杂的逻辑则需要通过更复杂的方式来实现,这就是所谓的“过程宏”​(procedural macro)​。它是直接用Rust语言写出来的,相当于一个编译器插件。但是编译器插件的最大问题是,它依赖于编译器的内部实现方式。一旦编译器内部有所变化,那么对应的宏就有可能出现编译错误,需要修改。因此,Rust中的“宏”一直难以稳定。