Appearance
9.4 常用的宏开发库
在前面的例子中使用了proc_macro
,但是对于proc_macro
有如下缺点:
- 需要在
proc-macro
类型的 crate 中才能使用,必须位于它们自己的 library crate 中。 - 不太好进行测试
proc-macro2 解决了这些问题,api 也基本兼容。
proc-macro2
、quote
和 syn
目前是 Rust 生态系统中最受欢迎的三个宏工具库。接下来我会分别介绍一下这三个库,并提供相应的文档链接和示例代码。
proc-macro2:这个库提供了一组工具来处理
proc_macro::TokenStream
类型,方便高效地实现自定义操作。具体而言,这个库可以让您更好地解析、操作和生成属性宏和过程宏。示例代码:
rustuse proc_macro2::{Span, Ident}; use quote::quote; #[proc_macro] pub fn my_macro(input: TokenStream) -> TokenStream { let ident = Ident::new("foo", Span::call_site()); quote! { struct #ident { field: i32, } impl #ident { pub fn new() -> Self { Self { field: 0 } } } } }
quote:这个库帮助您以简单、清晰的方式构造 Rust 代码的字符表示形式(也称为语法树)。通过 Rust 代码模板来生成完整的 Rust 代码,避免手动构造样板代码的麻烦。
示例代码:
rustuse quote::quote; fn main() { let my_ident = quote! { x }; let my_value = quote! { 42 }; let tokens = quote! { let #my_ident = #my_value; }; println!("{}", tokens); }
syn:这个库允许您解析和操作 Rust 代码的语法树,让您轻松地分析、修改和生成 Rust 代码。 与
proc-macro2
和quote
配合使用,可以编写更加复杂且功能强大的属性宏和过程宏。示例代码:
rustuse syn::parse_macro_input; use syn::DeriveInput; use quote::quote; #[derive(Debug)] struct MyStruct { field1: i32, field2: String, } impl MyStruct { fn new() -> Self { Self { field1: 0, field2: String::new(), } } } #[proc_macro_derive(MyDerive)] pub fn my_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = parse_macro_input!(input as DeriveInput); let name = &ast.ident; let my_struct_name = format!("MyStruct{}", name); let my_struct_ident = syn::Ident::new(&my_struct_name, name.span()); let gen = quote! { impl #name { pub fn into_my_struct(self) -> #my_struct_ident { #my_struct_ident { field1: self.field1, field2: self.field2.to_string(), } } } }; gen.into() }
综上,这三个宏工具库是 Rust 生态系统中最常用的库之一,非常适合编写属性宏和过程宏。您可以根据自己的需求选择使用它们中的一个或多个,并结合实际需求进行开发和编写代码。
通过这三个工具的结合:定义的过程宏首先被解析为proc_macro::TokenStream
,接着再被解析为proc_macro2::TokenStream
, 再使用 syn 库解析为 AST (这里的不同于编译生成的 AST ),最后再通过 quote 解析为TokenStream
,最终会被扩展到编译过程TokenStream
中,进而再被编译 AST 。
更多的学习可以到:David Tolnay的“Rust Latam:过程宏研讨会” Rust的宏(二)