Rust整个编译运行机制有很大程度上与合理使用宏相关。如println!、writeln!等即为控制输入输出的宏。下面给出几个例子:
- println与writeln
- 宏简化了对fmt输出流进行写入的语句
- vec![…]
- 宏简化了数组的定义
- 实际展开后的简化语句(实际上还有预分配代码)
let mut v = Vec::new()
for expr in (...) {v.push(expr)}
v
下面进入正题
Rust宏之声明宏(declarative macros)
声明宏即用宏来替换代码,与C/C++类似。
一个简单的vec![]
宏可以如下定义:
1
2
3
4
5
6
7
8
9
10
11
12
#[macro_export]
macro_rules! vec {
( $( $x:expr ),* ) => {
{
let mut temp_vec = Vec::new();
$(
temp_vec.push($x);
)*
temp_vec
}
};
}
当vec![1,2,3]
来调用这个宏时,会被替换为:
1
2
3
4
5
let mut temp_vec = Vec::new();
temp_vec.push(1);
temp_vec.push(2);
temp_vec.push(3);
temp_vec
代码中有点像match
语句,如果与()内的内容相匹配,则会执行{}内的代码。
Rust宏之过程宏(procedural macros)
过程宏将一段Rust代码作为输入,产生另一部分Rust代码作为输出,展开到文件中。比如可以用来注入默认实现。
需要在Cargo.toml中写入:
1
2
[lib]
proc-macro = true
过程宏之derive宏
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// derive宏
#[proc_macro_derive(MyMacro)]
pub fn my_macro(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).unwrap();
impl_hello_macro(&ast)
}
fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let gen = quote! {
impl MyMacro for #name {
fn hello_macro() {
println!("Hello, Macro! My name is {}", stringify!(#name));
}
}
};
gen.into()
}
#[derive(MyMacro)]
struct MyStruct;
上面两个相当于给MyStruct注入了MyMacro Traits的默认实现方法,也是编写自定义derive宏。
过程宏之类属性宏
与derive过程宏不同的是,类属性宏允许有属性。其他都一样。
一个很常见的例子,比如在web服务器中,被GET
访问/
url时,执行一个index
函数。可以这么写:
1
2
#[route(GET, "/")]
fn index() {
其中route是我们定义的宏,这个宏的函数头为:
1
2
#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
其中attr可以获取到属性GET与”/”。
过程宏之类函数宏
与macro_rules!
类似,但是macro_rules!
只能实现上面提到的“匹配-替换”。
1
2
3
4
5
6
// 函数宏,执行func_macro()即可声明一个函数。
#[proc_macro]
pub fn func_macro(input: TokenStream) -> TokenStream {
"fn func(){println!(\"hello world from func macro!\")}".parse().unwrap()
}
上方相当于定义了一个func()函数,用来println。