目录
一,宏的分类
二,声明宏
1,声明宏的匹配语法
2,匹配范畴
3,声明宏的调用
4,多入参匹配
5,常见声明宏
6,声明宏的卫生性
三,函数宏
四,属性宏
五,派生宏
一,宏的分类
rust的宏分为声明宏、过程宏。
过程宏又分为函数宏、属性宏、派生宏
二,声明宏
1,声明宏的匹配语法
声明宏的作用类似于代码替换,匹配语法和match很像。
macro_rules! my_show {
() => {
println!("run to here");
};
($sth:expr)=>{
println!("run to here {}", $sth);
}
}
fn gcd(x:i32, y:i32) ->i32{
if y < 0 {
my_show!(y);
return gcd(-x,-y);
}
if y == 0 {
my_show!();
return x;
}
return gcd(y, x%y);
}
fn main() {
gcd(15,-18);
println!("{:#?}",5);
}
2,匹配范畴
上面的$sth:expr表示匹配到一个表达式,expr是表示表达式这个范畴。
- item:语言项。比如一个函数、结构体、模块等
- block:代码块。比如一系列由花括号包裹的表达式和语句
- stmt:语句。比如一个赋值语句
- pat:模式
- expr:表达式。
- ty:类型。比如 Vec
- ident:标识符。比如一个变量名
- path:路径。比如:foo、
::std::mem::replace
、transmute::<_, int>
- meta:元数据。一般是在 #[...] 和 #![...] 属性内部的数据
- tt:单个的 token 树
- vis:可能为空的一个 Visibility 修饰符。比如 pub、pub(crate)
- literal :字面量
- lifetime :生命周期
3,声明宏的调用
!()也可以 ![]也可以 !{}也可以
4,多入参匹配
macro_rules! my_vec {
() => {
Vec::new();
};
($x:expr;$n:expr,$($y:expr),*)=>({
let mut v = Vec::new();
for i in 0..$n {
v.push($x);
}
$(v.push($y);)*
v
});
}
fn main() {
let v:Vec<i32> = my_vec!();
assert_eq!(v.len(), 0);
let v = my_vec!(1;6,2,3,4);
assert_eq!(v, Vec::from([1,1,1,1,1,1,2,3,4]));
println!("end");
}
这里其实相当于是传了3个参数,1和6之间用;连接, 6和“2,3,4”之间有逗号连接,用什么连接取决于宏的匹配格式里面写的是什么。
而2,3,4匹配的是$($y:expr),*这个类似正则表达式的东西,表示可以匹配任意多个表达式,用,连接起来
5,常见声明宏
println:
macro_rules! println {
() => {
$crate::print!("\n")
};
($($arg:tt)*) => {{
$crate::io::_print($crate::format_args_nl!($($arg)*));
}};
}
assert_eq:
macro_rules! assert_eq {
($left:expr, $right:expr $(,)?) => {
match (&$left, &$right) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = $crate::panicking::AssertKind::Eq;
// The reborrows below are intentional. Without them, the stack slot for the
// borrow is initialized even before the values are compared, leading to a
// noticeable slow down.
$crate::panicking::assert_failed(kind, &*left_val, &*right_val, $crate::option::Option::None);
}
}
}
};
($left:expr, $right:expr, $($arg:tt)+) => {
match (&$left, &$right) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = $crate::panicking::AssertKind::Eq;
// The reborrows below are intentional. Without them, the stack slot for the
// borrow is initialized even before the values are compared, leading to a
// noticeable slow down.
$crate::panicking::assert_failed(kind, &*left_val, &*right_val, $crate::option::Option::Some($crate::format_args!($($arg)+)));
}
}
}
};
}
6,声明宏的卫生性
声明宏是半卫生宏,变量标识符不会造成符号冲突,符合卫生性。
但是在宏内部定义的泛型和生命周期标识符是不卫生的。