为什么宏里的 let tmp 不会弄坏调用处的 tmp?这个叫「卫生性」。这篇不靠概念,而是让编译器把每个标识符的语法上下文编号打印出来,再用四个编译与运行实验划清卫生的边界。
源码:
1 | macro_rules! swap { ($a:expr, $b:expr) => { let tmp = $a; $a = $b; $b = tmp; }; }
2 | fn main() {
3 | let mut x = 1; let mut y = 2; let tmp = 100;
4 | swap!(x, y);
5 | println!("x = {} y = {} tmp = {}(调用处的 tmp 没有被宏里的 tmp 覆盖)", x, y, tmp);
6 | let mut tmp2 = 5; let mut other = 6;
7 | swap!(tmp2, other);
8 | println!("tmp2 = {} other = {}", tmp2, other);
9 | }
编译:成功
运行(退出码 0):
x = 2 y = 1 tmp = 100(调用处的 tmp 没有被宏里的 tmp 覆盖)
tmp2 = 6 other = 5macro_rules 基础见声明宏 macro_rules!。
源码:
macro_rules! twice { ($e:expr) => {{ let v = $e; v + v }}; }
pub fn f() -> i32 { let v = 10; twice!(v + 1) }
展开(带 hygiene 标记 名字#上下文编号):
pub fn f /* 2332#0 */() -> i32 /* 985#0 */ {
let v /* 2348#0 */ = 10;
{
let v /* 2348#4 */ = v /* 2348#0 */ + 1;
v /* 2348#4 */ + v /* 2348#4 */
}
}
crate0::{{expn2}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "twice")
#4: parent: #0, outer_mark: (crate0::{{expn2}}, SemiOpaque)hygiene 打印用 RUSTC_BOOTSTRAP=1 -Z unpretty=expanded,hygiene;编号是该次编译的内部值,会随编译器版本变化。
源码:
1 | macro_rules! show_y { () => { println!("y = {}", y) }; }
2 | fn main() { let y = 5; show_y!(); }
编译:失败
error[E0425]: cannot find value `y` in this scope
--> <实验目录>/src/u78c.rs:1:50
|
1 | macro_rules! show_y { () => { println!("y = {}", y) }; }
| ^ not found in this scope
2 | fn main() { let y = 5; show_y!(); }
| --------- in this macro invocation
|
help: an identifier with the same name exists, but is not accessible due to macro hygiene
--> <实验目录>/src/u78c.rs:2:17
|
2 | fn main() { let y = 5; show_y!(); }
| ^
= note: this error originates in the macro `show_y` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0425`.
编译:成功
运行(退出码 0):
y = 5
宏里 let 传入的名字 z 在调用处可见: z = 9
源码:
1 | macro_rules! decl_hidden { ($val:expr) => { let hidden = $val; }; }
2 | fn main() { decl_hidden!(1); println!("{}", hidden); }
编译:失败
error[E0425]: cannot find value `hidden` in this scope
--> <实验目录>/src/u78e.rs:2:45
|
2 | fn main() { decl_hidden!(1); println!("{}", hidden); }
| ^^^^^^ not found in this scope
|
help: an identifier with the same name is defined here, but is not accessible due to macro hygiene
--> <实验目录>/src/u78e.rs:1:49
|
1 | macro_rules! decl_hidden { ($val:expr) => { let hidden = $val; }; }
| ^^^^^^
2 | fn main() { decl_hidden!(1); println!("{}", hidden); }
| --------------- in this macro invocation
= note: this error originates in the macro `decl_hidden` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0425`. 编译:成功
运行(退出码 0):
宏里定义的 helper 编译:成功
运行(退出码 0):
i = 0 宏里的 'outer 与调用处的 'outer 不冲突,找到 Some(8)
i = 1 宏里的 'outer 与调用处的 'outer 不冲突,找到 Some(8)