Rust宏的使用

学习rust有三大难点,所有权、生命周期、宏。一段时间学习下来感觉所有权还好整一点,生命周期和宏学习是真的离谱。下面简单梳理记录下rust中宏的学习和使用。

rust中宏有两大类:声明宏 和 过程宏,然后过程宏又分为三小类——Funtion-like macros、Derive macraos、Attribute macros.

声明宏(macro_rules!)的使用

声明宏类似于match关键字的使用。在macro_rules!内,可定义多种匹配模式,之后根据匹配模式输出对应的代码。

声明宏的使用:

//main.rs
macro_rules! add_fn {
    ($a:expr,$b:expr) => {
        {
            eprintln!("add_fn: {:?}",$a);
            eprintln!("add_fn: {:?}",$b);
            $a + $b
        }
    };
    ($a:expr,$b:expr,$c:expr) => {
        {
            $a+$b+$c
        }
    }
}

fn main() {
	prnintln!("{}",add_fn!(12,13)); // 输出25
  println!("{}",add_fn!(89.8,98.9));// 输出188.7

	println!("{}",add_fn!(10,11,12)); //输出33
}

过程宏可获取与其关联的TokenStream然后生成新的TokenStream。共有三种形式:

  1. Function-like macros(函数式宏),使用#[proc_macro]属性声明,类似于声明宏,但比声明宏更为强大。
  2. Derive macros(派生宏),使用#[proc_macro_derive(xxx)]属性声明,可用于struct、enum、union。
  3. Attribue macro(属性宏),使用#[proc_macro_attribute]属性声明,可用于function、struct、enum、union。与派生宏不同的是,属性宏使用时可以获取到属性内添加的参数,见示例 #[route(GET,"/index")] ,其中 GET、”index”都可被获取到。同时也可以获取到与其关联的函数或者结构体或者enum或者union的信息,然后生成新的TokenStream。

过程宏的使用

  • Function-like macros - custom!(...)
  • Derive macros - #[derive(CustomDerive)]
  • Attribute macros - #[CustomAttribute]

因为过程宏无法直接放在main的crate包内运行,所以我们需要在项目中里面建一个lib包,然后通过main.rs来引入该lib包进行测试。

命令行执行以下命令:

>>> cargo new test_macro_pro  //创建一个rust项目
>>> cd test_macro_pro
>>> cargo new sample --lib    //在该项目内创建一个lib包

创建完成后我们的目录长这样:

Rust宏的使用_第1张图片

目录创建完成之后我们需要在 test_macro_pro/sample/Cargo.toml 里面添加以输出包含macro的lib包:

...

[lib]
proc-macro = true

过程宏的使用:

Function-like macros:

// test_macro_pro/src/main.rs
use proc_macro::TokenStream;

#[proc_macro]
pub fn make_answer(item: TokenStream) -> TokenStream {
    eprintln!("make_answer:{:#?}",item); //可以通过eprintln!进行打印调试
    "fn answer() -> u32 { 45 }".parse().unwrap()
}

// test_macro_pro/src/main.rs
use sample::{make_answer};

make_answer!();

fn main() {
	println!("{}",answer()); //输出45
}

Derive macros:

因为会使用到quote、syn,所以需要在test_macro_pro/sample/Cargo.toml里面声明,如下:

[dependencies]
quote = "1.0"
syn = "2.0"

[lib]
proc-macro = true

测试代码如下:

//test_macro_pro/sample/src/lib.rs
extern crate proc_macro;

use proc_macro::TokenStream;
use quote::quote;
use syn;

#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
    // Construct a representation of Rust code as a syntax tree
    // that we can manipulate
    let ast = syn::parse(input).unwrap();

    // Build the trait implementation
    impl_hello_macro(&ast)
}

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

//test_macro_pro/src/main.rs
use sample::HelloMacro;

trait HelloMacro {
    fn hello_macro();
}

#[derive(HelloMacro)]
struct Pancakes;

fn main() {
    Pancakes::hello_macro(); //输出:Hello, Macro! My name is Pancakes!
}

Attribue macro:

//test_macro_pro/sample/src/lib.rs
extern crate proc_macro;

use proc_macro::TokenStream;

#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
    eprintln!("route attr:{:#?}", attr);
    eprintln!("route item:{:#?}", item);
    item
}

//test_macro_pro/src/main.rs
use sample::route;

#[route(GET,"/index")]  //执行cargo check ,route attr 会打印出GET、index的信息
fn test_route() {}     

fn main() {
	
}

你可能感兴趣的:(rust,区块链)