简介
主要是给一类结构体实现一个没啥 luan 用的 derive 宏,因为开发过程中发觉某一类结构体,一定要实现某个 trait,而且这些 trait 没有啥复杂逻辑,但是必须要实现,等于讲就是要写固定模板代码,既然如此,宏就非常适合做这事。
本来是打算写个规则宏了事,不过写规则宏感觉东一块西一块的,不够高内聚。
创建项目
开始之前,先把项目创建好(项目名字不重要,我只是要在另一个项目中用到它,所以用这个名字。)
cargo new objc-derive
修改 Cargo.toml 文件内容为下面这些
[package]
name = "objc-derive"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = true
[dependencies]
quote = "1.0"
proc-macro2 = "1.0"
syn = { version = "2", features = ["full"] }
目标使用方法
最终我们要实现一个这样的效果
use objc_derive::Message;
pub mod objc {
pub trait Message {
fn send(&self) -> String;
}
}
#[derive(Message)]
struct Person {
name: String,
}
impl Person {
pub fn new(name: &str) -> Person {
Person { name: name.to_string() }
}
}
fn main() {
let person = Person::new("test");
println!("{}", person.send());
}
其实就是自动给此处的 Person 实现 Message trait
impl Message for Person {
fn send(&self) -> String {
self.name.clone()
}
}
所以,先把 derive 宏的函数体写一下
#[proc_macro_derive(Message)]
pub fn message(i: TokenStream) -> TokenStream {
TokenStream::new()
}
然后使用之前弄好的两个第三方包来处理输入的 i
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(Message)]
pub fn message(i: TokenStream) -> TokenStream {
let syntax_tree = parse_macro_input!(i as DeriveInput);
TokenStream::new()
}
接下来就是处理 DeriveInput 类型的 syntax_tree 生成新代码
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(Message)]
pub fn message(i: TokenStream) -> TokenStream {
let syntax_tree = parse_macro_input!(i as DeriveInput);
match generate(syntax_tree) {
Ok(ts) => ts.into(),
Err(e) => e.to_compile_error().into(),
}
}
fn generate(syntax_tree: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
let ident = syntax_tree.ident;
Ok(quote::quote!(
use objc::Message;
impl objc::Message for #ident {
fn send(&self) -> String {
self.name.clone()
}
}
))
}
主要是用到 quote! 这个宏,然后把对应的 trait 给实现了一下,逻辑很简单。
其实是给另一篇文章做铺垫用的,就当水文吧。