前言
rust声明宏基本上就是直接简单的替换,类似于c的define。而过程宏则类似于java的注解编程。通过注解标签,标记生成特定的代码。
正文
但是因为过程宏可以生成代码。所以需要单独的模块。需要单独的crate。并且该crate必须标记用来处理过程宏。需要再cargo.tomal添加标签
argo
[lib]
proc-macro = true
doctest = false
而大部分的模块需要提供函数:
rust
#[proc_macro_derive(OBU_Parser, attributes(obu))]
#[proc_macro_error]
pub fn parser(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
//
let token_stream = expand_filed(input);
//
token_stream
.unwrap_or_else(|e| e.into_compile_error())
.into()
// TokenStream::new()
}
使用时候主要是:
java
#[derive(OBU_Parser)]
struct color_info {
#[obu(len = -3)]
r: u8,
}
这里会把
rust
struct color_info {
r: u8,
}
转化成TokenStream,传给函数,然后最后生成TokenStream。替换原来的struct。
如何解析input的TokenStream,生成我们需要的TokenStream,下面我们写一些测试代码:
主要通过syn解析tokenstream
rust
use quote::quote;
use syn::__private::ToTokens;
use syn::{parse_quote, File, Item};
extern crate quote;
struct X {
x: i32,
}
fn main() {
let code = quote! {
struct X {
x: Vec<i32>,
}
};
let mut syntax_tree: File = syn::parse2(code).unwrap();
syntax_tree.items = syntax_tree.items.into_iter().filter(|item| {
matches!(item, Item::Struct(_))
}).collect();
for item in syntax_tree.items {
match item {
Item::Struct(item_fn) => {
item_fn.fields.iter().for_each(|field| {
println!("file: {:?}", field);
match &field.ty{
syn::Type::Path(path) => {
println!("{:?}", path.path.segments[0].ident);
println!("arguments:{:?}", path.path.segments[0].arguments);
let ident = path.path.segments[0].ident.to_string();
if ident == "i32" {
println!("i32 field {:?}", field.ident);
}else if ident == "Vec" {
println!("Vec field {:?}", field.ident);
}
}
_ => {
println!("else {:?}", field.ty);
}
}
});
//将item_fn转换成原始代码字符串
let code = quote! { #item_fn }.to_string();
println!("{}", code);
// fn_list.push(code);
}
_ => {}
}
}
}
后记
这里主要简要记录一下过程宏的。