Rust宏

过程性宏

Posted by MetaNetworks on March 2, 2022
本页面总访问量

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。