P.MAC.PRO.01 定义和使用过程宏时,应保证卫生性
【描述】
过程宏在展开时,有时可能会直接引用输入代码中的标识符(变量、函数名等)。如果过程宏引入了与周围代码冲突的标识符,就会导致不卫生的情况。为了避免这种情况,通常需要对输入标识符进行重命名。
要避免不卫生的情况,包括以下常用方法:
a. 确保宏生成代码的符号与外部有差异,如增加_
前缀。
b. 对库中程序项使用绝对路径.
【反例】
Rust
// crate guide
#[proc_macro]
pub fn my_macro(input: TokenStream) -> TokenStream {
let expanded = quote! {
// 不符合:直接使用外部的变量 x
x = 999;
// 不符合: 宏内定义的变量 local 被外部使用
let local = "hello";
// ...
};
expanded.into()
}
// crate b
fn main() {
let mut x = 0;
let local = "";
guide::my_macro!();
assert_eq!(x, 999);
assert_eq!(local, "hello");
}
【正例】
Rust
#[proc_macro]
pub fn my_macro(input: TokenStream) -> TokenStream {
let expanded = quote! {
// 符合:方法 a. 宏内变量增加 ` ` 前缀
let _x = 0;
let _local = "hello";
// ...
};
expanded.into()
}
【正例】
过程宏生成的代码宜使用绝对路径,防止命名冲突产生意想不到的后果。 可以配合使用 #![no_implicit_prelude]
属性来验证标识符是否都使用了绝对路径:
Rust
#![no_implicit_prelude]
#[derive(MyMacro)]
struct A;
Rust
// 符合:方法 b. 使用绝对路径
quote!(::std::string::ToString::to_string(a))
Rust
quote! {{
// 符合:方法 b. 使用绝对路径
use ::std::string::ToString;
a.to_string()
}}