Rust中的宏

  1. Rust中的宏主要有两种,一种是使用macro_rules!的声明宏,一种是过程宏,过程宏又
    分成三种:
    (1) 自定义宏#[derive],在结构体、枚举等上指定通过derive属性添加代码
    (2) 类属性宏,定义可以用于任意项的自定义属性
    (3) 类函数宏,看起来像是函数,但是作用于作为参数传递的Token

  2. 宏和函数
    (1) 宏是一种为写其他代码而写代码的方式,对于减少大量编写代码和维护代码非常有用
    (2) 一个函数标签必须声明函数参数的个数和类型,宏只接受可变参数
    (3) 宏的定义比函数定义更加复杂
    (4) 调用宏之前,必须定义并且将其引入作用域,函数但是可以在任何地方调用和定义

声明宏

1
let v = vec![1,2,3];

创建自己的声明宏

1
2
3
mkdir learn
cd learn
vim Cargo.toml
  • 之后,进行设置
1
2
3
4
5
6
//learn/Cargo.toml
[workspace]
members = [
"mac",
"main",
]
  • 创建mac lib
1
2
3
cargo new mac --lib
cd mac
vim src/lib.rs
  • 设置mac lib
1
2
3
4
5
6
7
8
9
10
11
12
#[macro_export]
macro_rules! my_vec {//*代表对这个表达式匹配有0个或者多个,模仿vec!
($($x: expr), *) => {
{let mut temp_vec = Vec::new();
$(
temp_vec.push($x);
)*//使用$()*这个语法来捕捉0个或者多个相同的语法
temp_vec
}
};
}

  • 创建main
1
2
3
4
# /learn
cargo new main
cd main
vim Cargo.toml
  • 修改main
1
2
[dependencies] 
mac = {path = "../mac"}
  • 写主函数
1
vim src/main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
# learn/main/src/main.rs
use mac;

fn main() {
let v = mac::my_vec![1,2,3];
//等价于
//let mut temp_vec = Vec::new();
//temp_vec.push(1);
//temp_vec.push(2);
//temp_vec.push(3);
//temp_vec
println!("v = {:?}",v);
}

过程宏

过程宏接受Rust代码作为输入,在代码上操作,产生另一些代码作为输出,非像声明宏那样
匹配对应的模式,然后以另一部分代码替换当前代码

定义过程宏函数接受一个TokenStream作为输入并产生一个TokenStream作为输出,也就是宏
的核心,宏所处理的源代码组成了输入TokenStream,同时宏生成的代码是输出TokenStream,
如下:

1
2
3
4
5
6
7
8
9
10
use proc_macro;
#[some_attribute]
pub fn some_name(input: TokenStream) -> TokenStream {

}

#[derive(Debug)] //过程宏当中的derive宏,fmt::Display trait
struct A {
a: i32,
}

自定义derive宏

名称必须为some some_derive这种,与例子相同

  • 首先创建hello_macro
1
2
3
cargo new hello_macro --lib
cd hello_macro
vim src/lib.rs
  • 之后进行编辑
1
2
3
pub trait HelloMacro {
fn hello_macro();
}
  • 之后在这个文件夹中新建
1
2
3
4
# 位置:hello_macro/
cargo new hello_macro_derive --lib
cd hello_macro_derive
vim Cargo.toml
  • 填写:
1
2
3
4
5
[lib]
proc-macro = true
[dependencies]
syn = "0.14.4"
quote = "0.6.3"
  • 实现代码填写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
extern crate proc_macro;
use crate::proc_macro::TokenStream;
use quote::quote;
use syn;

fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.indent;
let gen = quote! {
impl HelloMacro for #name {
fn hello_macro() {
println!("Hello,in my name is {}",stringify!(#name));
}
}
};

gen.into()
}
#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
let ast = syn::parse(input).unwrap();//将其解析成结构体,叫做DeriveInput
//
impl_hello_macro(&ast);
}
  • 使用宏的代码
1
2
3
4
5
cd ..
# /
cargo new main
cd main
vim Cargo.toml
  • 编写依赖
1
2
3
[dependencies]
hello_macro = {path = "../hello_macro"}
hello_macro_derive = {path = "../hello_macro_derive"}
  • 编写代码
1
2
3
4
5
6
7
8
9
10
11
//main/src/main.rs
use hello_macro::HelloMacro;
use hello_macro_derive::HelloMacro;

#[derive(HelloMacro)]
struct Main;

fn main() {
Main::hello_macro(); //调用实现的hello_macro特征

}

类属性宏

类属性宏和derive宏类似,但是不同于为derive属性生成代码,允许创建新的属性,通过接受输入的
标记替换为新的标记,派生宏则是接受输入的TokenTree,然后追加新的标记树

  1. 例子:
    创建一个名为route的属性用于注解web应用程序框架(web application framework)的函数:
1
2
#[route(GET,"/")]
fn index() {

(1) route属性将由框架本身定义为一个过程宏,宏定义签名函数:

1
2
#[proc_macro_attribute] 
pub fn route(attr: TokenStream,item: TokenStream) -> TokenStream {

属性宏接受两个参数,一个是attr,类似#[allow(unused)]中的unused,另一个是item,是被属性
修饰的item,如结构体的定义

(2) 类属性工作宏和自定义derive宏工作方式相同

类函数宏