Skip to content

9.4 常用的宏开发库

在前面的例子中使用了proc_macro,但是对于proc_macro有如下缺点:

  1. 需要在proc-macro类型的 crate 中才能使用,必须位于它们自己的 library crate 中。
  2. 不太好进行测试

proc-macro2 解决了这些问题,api 也基本兼容。

proc-macro2quotesyn 目前是 Rust 生态系统中最受欢迎的三个宏工具库。接下来我会分别介绍一下这三个库,并提供相应的文档链接和示例代码。

  1. proc-macro2:这个库提供了一组工具来处理 proc_macro::TokenStream 类型,方便高效地实现自定义操作。具体而言,这个库可以让您更好地解析、操作和生成属性宏和过程宏。

    示例代码:

    rust
    use 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 }
                }
            }
        }
    }
  2. quote:这个库帮助您以简单、清晰的方式构造 Rust 代码的字符表示形式(也称为语法树)。通过 Rust 代码模板来生成完整的 Rust 代码,避免手动构造样板代码的麻烦。

    示例代码:

    rust
    use quote::quote;
    
    fn main() {
        let my_ident = quote! { x };
        let my_value = quote! { 42 };
        let tokens = quote! {
            let #my_ident = #my_value;
        };
        println!("{}", tokens);
    }
  3. syn:这个库允许您解析和操作 Rust 代码的语法树,让您轻松地分析、修改和生成 Rust 代码。 与 proc-macro2quote 配合使用,可以编写更加复杂且功能强大的属性宏和过程宏。

    示例代码:

    rust
    use 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的宏(二)

Released under the MIT License