Rust中的宏
-
Rust中的宏主要有两种,一种是使用macro_rules!的声明宏,一种是过程宏,过程宏又
分成三种:
(1) 自定义宏#[derive],在结构体、枚举等上指定通过derive属性添加代码
(2) 类属性宏,定义可以用于任意项的自定义属性
(3) 类函数宏,看起来像是函数,但是作用于作为参数传递的Token
-
宏和函数
(1) 宏是一种为写其他代码而写代码的方式,对于减少大量编写代码和维护代码非常有用
(2) 一个函数标签必须声明函数参数的个数和类型,宏只接受可变参数
(3) 宏的定义比函数定义更加复杂
(4) 调用宏之前,必须定义并且将其引入作用域,函数但是可以在任何地方调用和定义
声明宏
创建自己的声明宏
1 2 3
| mkdir learn cd learn vim Cargo.toml
|
1 2 3 4 5 6
| [workspace] members = [ "mac", "main", ]
|
1 2 3
| cargo new mac --lib cd mac vim src/lib.rs
|
1 2 3 4 5 6 7 8 9 10 11 12
| #[macro_export] macro_rules! my_vec { ($($x: expr), *) => { {let mut temp_vec = Vec::new(); $( temp_vec.push($x); )* temp_vec } }; }
|
1 2 3 4
| cargo new main cd main vim Cargo.toml
|
1 2
| [dependencies] mac = {path = "../mac"}
|
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]; 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)] struct A { a: i32, }
|
自定义derive宏
名称必须为some some_derive这种,与例子相同
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
| 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(); 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
| use hello_macro::HelloMacro; use hello_macro_derive::HelloMacro;
#[derive(HelloMacro)] struct Main;
fn main() { Main::hello_macro(); }
|
类属性宏
类属性宏和derive宏类似,但是不同于为derive属性生成代码,允许创建新的属性,通过接受输入的
标记替换为新的标记,派生宏则是接受输入的TokenTree,然后追加新的标记树
- 例子:
创建一个名为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宏工作方式相同
类函数宏