很多编程语言里都没有宏,不过C语言里面是有宏,比如:
#define CALC_SUN(x) 5 * x + x*x + abs(x)
这样在程序里面我们就可以写:
CALC_SUN(2)
然后在编译的时候,系统就会自动展开为 5 _2 + 2 2_ + abs(2). 相比函数而言,这个少了一次调用。不用跳转也不用return。增加了编译的成本,但是提升了运行的性能。
不过C的宏功能很弱,如果你把 2 写成 1+1, 那么因为*号优先级大于+号,会得出一个错误结果。
Rust的宏不但没有这个问题,并且应用也十分广泛,别的不说, 就是最简单的hello world都会用到宏:
fn main() {
println!("hello world!");
}
这里需要注意的是println!
非println
,多了感叹号!
,后缀不带感叹号为普通函数,带感叹号的为宏函数,rust不存在println
普通函数。看rust的源代码,基本上都是宏,各种感叹号层出不穷。就连定义宏的的方法也是用一个宏去定义!
macro_rules! mymacro {
() =>{}
}
上面这个就定义了一个什么都不做的宏。不过基本的结构还是在的。宏的结构由 一系列的() =>{} 构成,前面的小括号里面的叫做pattern,一旦传入的参数和这个pattern match成功了,就展开成后面大括号里面的效果。
别的不说,就连最基本的数据结构,数组向量在rust里面都是一个宏,vec![1, 2,3], 其背后的逻辑是:
macro_rules! vec {
( $( $x:expr ),* ) => {
{
let mut temp_vec = Vec::new();
$(
temp_vec.push($x);
)*
temp_vec
}
};
}
$x 是个代数,冒号后面跟的是类型,也就是expression,因为1,2,3, 2+3, 这种都是expression。
所以$x:expr 的意思就是一个expression,用$x表示。星号 和regular expression 一样,表示0个或者多个的重复,所以$($x:expr), 就是($x:expr) 重复0次或者多次。
一旦这个模式得到了匹配,就执行后面的内容, let mut temp_vec = Vec::new() 是建立一个temp_vec的空向量,然后下面的$(temp_vec.push($x);)* 表示展开的时候把 temp_vec.push($x) 重复相应的次数。最后一个temp_vec 则是返回值,前面隐藏了一个return。