学习rust有三大难点,所有权、生命周期、宏。一段时间学习下来感觉所有权还好整一点,生命周期和宏学习是真的离谱。下面简单梳理记录下rust中宏的学习和使用。
rust中宏有两大类:声明宏 和 过程宏,然后过程宏又分为三小类——Funtion-like macros、Derive macraos、Attribute macros.
声明宏类似于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。共有三种形式:
因为过程宏无法直接放在main的crate包内运行,所以我们需要在项目中里面建一个lib包,然后通过main.rs来引入该lib包进行测试。
命令行执行以下命令:
>>> cargo new test_macro_pro //创建一个rust项目
>>> cd test_macro_pro
>>> cargo new sample --lib //在该项目内创建一个lib包
创建完成后我们的目录长这样:
目录创建完成之后我们需要在 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() {
}