macro_rules! 是 Rust 最常用的宏机制,但它的规则、展开和陷阱都不直观。这篇从最小的例子开始,让编译器展开宏,验证表达式被重复求值的坑,以及编译错误长什么样。
源码:
1 | macro_rules! square { ($x:expr) => { $x * $x }; }
2 | macro_rules! my_vec {
3 | () => { Vec::new() };
4 | ($($x:expr),+ $(,)?) => {{ let mut v = Vec::new(); $( v.push($x); )+ v }};
5 | }
6 | macro_rules! maxm {
7 | ($x:expr) => { $x };
8 | ($x:expr, $($rest:expr),+) => {{ let a = $x; let b = maxm!($($rest),+); if a > b { a } else { b } }};
9 | }
10 | fn main() {
11 | println!("square!(7) = {}", square!(7));
12 | let e: Vec<i32> = my_vec![]; let v = my_vec![1, 2, 3,];
13 | println!("my_vec![] = {:?} my_vec![1,2,3,] = {:?}", e, v);
14 | println!("maxm!(3, 9, 4, 7) = {} maxm!(5) = {}", maxm!(3, 9, 4, 7), maxm!(5));
15 | }
编译:成功
运行(退出码 0):
square!(7) = 49
my_vec![] = [] my_vec![1,2,3,] = [1, 2, 3]
maxm!(3, 9, 4, 7) = 9 maxm!(5) = 5 源码:
macro_rules! square { ($x:expr) => { $x * $x }; }
macro_rules! my_vec { ($($x:expr),+) => {{ let mut v = Vec::new(); $( v.push($x); )+ v }}; }
pub fn demo() -> (i32, Vec<i32>) { (square!(2 + 3), my_vec![1, 2, 3]) }
展开后:
macro_rules! square { ($x:expr) => { $x * $x }; }
macro_rules! my_vec {
($($x:expr),+) => {{ let mut v = Vec::new(); $(v.push($x);)+ v }};
}
pub fn demo() -> (i32, Vec<i32>) {
((2 + 3) * (2 + 3),
{ let mut v = Vec::new(); v.push(1); v.push(2); v.push(3); v })
}展开用 RUSTC_BOOTSTRAP=1 与 -Z unpretty=expanded;输出里的括号来自编译器对表达式片段的打印。宏的卫生性见宏卫生性。
编译:成功
运行(退出码 0):
square!(2 + 3) = 25(2+3 被当作整体)
square!(next()) = 2:宏把 next() 展开了两次(1 * 2),调用次数 2
square_fn(next()) = 1:函数只求值一次,调用次数 1 源码:
1 | macro_rules! pair { ($a:expr, $b:expr) => { ($a, $b) }; }
2 | fn main() { let p = pair!(1); println!("{:?}", p); }
编译:失败
error: unexpected end of macro invocation
--> <实验目录>/src/u77d.rs:2:28
|
1 | macro_rules! pair { ($a:expr, $b:expr) => { ($a, $b) }; }
| ----------------- when calling this macro
2 | fn main() { let p = pair!(1); println!("{:?}", p); }
| ^ missing tokens in macro arguments
|
note: while trying to match `,`
--> <实验目录>/src/u77d.rs:1:29
|
1 | macro_rules! pair { ($a:expr, $b:expr) => { ($a, $b) }; }
| ^
error: aborting due to 1 previous error
源码:
1 | fn main() { println!("{}", late!(2)); }
2 | macro_rules! late { ($x:expr) => { $x + 1 }; }
编译:失败
error: cannot find macro `late` in this scope
--> <实验目录>/src/u77e.rs:1:28
|
1 | fn main() { println!("{}", late!(2)); }
| ^^^^ consider moving the definition of `late` before this call
|
note: a macro with the same name exists, but it appears later
--> <实验目录>/src/u77e.rs:2:14
|
2 | macro_rules! late { ($x:expr) => { $x + 1 }; }
| ^^^^
warning: unused macro definition: `late`
--> <实验目录>/src/u77e.rs:2:14
|
2 | macro_rules! late { ($x:expr) => { $x + 1 }; }
| ^^^^
|
= note: `#[warn(unused_macros)]` (part of `#[warn(unused)]`) on by default
error: aborting due to 1 previous error; 1 warning emitted 编译:成功
运行(退出码 0):
seven() = 7 forty_two() = 42
Point { x: 0, y: 0, tag: "" }
op!(3 plus 4) = 7 op!(3 times 4) = 12
count!(a b c d e) = 5