过程宏
属性宏继承宏函数宏
找了一个19年的文章,不知道代码还能不能跑,但是写的内容合适,看了基本就知道咋回事了 主要突出一个理解概念 https://dengjianping.github.io/2019/02/28/%E5%A6%82%E4%BD%95%E7%BC%96%E5%86%99%E4%B8%80%E4%B8%AA%E8%BF%87%E7%A8%8B%E5%AE%8F(proc-macro).html
属性宏
#[proc_macro_attribute]
pub fn run_time(_: TokenStream, func: TokenStream) -> TokenStream {
let func = parse_macro_input!(func as ItemFn);
let func_vis = &func.vis; // like pub
let func_block = &func.block; // { some statement or expression here }
let func_decl = func.sig;
let func_name = &func.ident; // function name
let func_generics = &func_decl.generics;
let func_inputs = &func_decl.inputs;
let func_output = &func_decl.output;
let caller = quote!{
// rebuild the function, add a func named is_expired to check user login session expire or not.
#func_vis fn #func_name #func_generics(#func_inputs) #func_output {
use std::time;
let start = time::Instant::now();
#func_block
println!("time cost {:?}", start.elapsed());
}
};
caller.into()
}
#[run_time]
fn deco(t: u64) {
let secs = Duration::from_secs(t);
thread::sleep(secs);
}
// ...
deco(6);
这个其实像ts里那个装饰器,看代码 func_vis 这类的从名字和注释能看出来是啥,然后,要做的是修改函数内容,#是把字符串变成真的!这里就会出现在start之后,执行原有的函数内容,也就是那个func_block,然后继续执行println!
继承宏
这个例子是实现了一个打印结构体的功能
#[proc_macro_derive(Show)]
pub fn derive_show(item: TokenStream) -> TokenStream {
// 解析整个token tree
let input = parse_macro_input!(item as DeriveInput);
let struct_name = &input.ident; // 结构体名字
// 提取结构体里的字段
let expanded = match input.data {
Data::Struct(DataStruct{ref fields,..}) => {
if let Fields::Named(ref fields_name) = fields {
// 结构体中可能是多个字段
let get_selfs: Vec<_> = fields_name.named.iter().map(|field| {
let field_name = field.ident.as_ref().unwrap(); // 字段名字
quote! {
&self.#field_name
}
}).collect();
let implemented_show = quote! {
// 下面就是Display trait的定义了
// use std::fmt; // 不要这样import,因为std::fmt是全局的,无法做到卫生性(hygiene)
// 编译器会报错重复import fmt当你多次使用Show之后
impl std::fmt::Display for #struct_name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
// #(#get_self),*,这是多重匹配,生成的样子大概是这样:&self.a, &self.b, &self.c, ...
// 用法和标准宏有点像,关于多个匹配,可以看这个文档
// https://docs.rs/quote/1.0.0/quote/macro.quote.html
write!(f, "{} {:?}", stringify!(#struct_name), (#(#get_selfs),*))
}
}
};
implemented_show
} else {
panic!("sorry, may it's a complicated struct.");
}
}
_ => panic!("sorry, Show is not implemented for union or enum type.")
};
expanded.into()
}
use your_crate_name::Show;
// ...
#[derive(Show)]
struct MySelf {
name: String,
age: u8,
}
// ...
let me = MySelf{name: "Jamie", age: 255};
println!("{}", me); // MySelf (Jamie, 255)
我们直接看他代码写的啥,他如果是这个 Data::Struct(DataStruct{ref fields,…}),然后就从这个fields里找name,然后是把name收集起来,get_selfs是一个vec,他用来装一下结果,然后在后面为这个结构体实现一个display trait,打印出来get_selfs,返回implemented_show,实现了这个trait后面就可以打印他了
函数宏
#[proc_macro]
pub fn my_proc_macro(ident: TokenStream) -> TokenStream {
let new_func_name = format!("test_{}", ident.to_string());
let concated_ident = Ident::new(&new_func_name, Span::call_site()); // 创建新的ident,函数名
let expanded = quote! {
// 不能直接这样写trait bound,T: Debug
// 会报错,找不到Debug trait,最好给出full path
fn #concated_ident
println!("{:?}", t);
}
};
expanded.into()
}
这个好像就比较素,直接修改函数名字,函数内容啥的
推荐文章
发表评论