P.MAC.DCL.01 不要将声明宏内的变量作为外部变量使用
【描述】
声明宏是半卫生(semi-hygienic)宏,其内部元变量(metavariables)不可作为外部变量去使用。但是对于泛型参数(包括生命周期参数)是不卫生的,所以要小心使用。
【反例】
下面为卫生场景示例:
macro_rules! using_a { ($e:expr) => { { let a = 42; $e } } } fn main() { let four = using_a!(a / 10); // build error: cannot find value `a` in this scope }
下面为不卫生场景示例:
trait FooTrait { fn get(&self) -> i32; } // 使用宏为带生命周期的类型实现 FooTrait macro_rules! impl_FooTrait { ($name:ty) => { // 这里使用的 'a 是宏内部定义 impl<'a> $crate::FooTrait for $name { fn get(&self) -> i32 { *self.0 } } }; } struct Baz<'a>(&'a i32); impl_FooTrait!(Baz<'a>); // 这里的 'a 是宏外部 // 整个程序正常编译运行,说明宏内外的 'a 被共用,不卫生 fn main() { let val = 20; let baz = Baz(&val); method(&baz); } // 测试实现 FooTrait 的方法 fn method(foo: &dyn FooTrait) { println!("{:?}", foo.get()); }
【正例】
下面为卫生场景示例:
macro_rules! using_a { ($a:ident, $e:expr) => {{ let $a = 42; $e }}; } fn main() { let four = using_a!(a, a / 10); }
下面为不卫生场景示例:
trait FooTrait { fn get(&self) -> i32; } // 使用宏为带生命周期的类型实现 FooTrait macro_rules! impl_FooTrait { // 这里不直接使用宏内部定义的 'a ,而从外面通过 $lifetime 传入 // 是为了避免不卫生可能引发的问题 ($name:ty, $lifetime:tt) => { impl<$lifetime> $crate::FooTrait for $name { fn get(&self) -> i32 { *self.0 } } }; } struct Baz<'a>(&'a i32); impl_FooTrait!(Baz<'a>, 'a); // 这里的 'a 是从宏外部传入到宏内 // 整个程序正常编译运行 fn main() { let val = 20; let baz = Baz(&val); method(&baz); } // 测试实现 FooTrait 的方法 fn method(foo: &dyn FooTrait) { println!("{:?}", foo.get()); }