如何编写自定义的派生宏?

44

我正在尝试在Rust中编写自己的派生模式宏,并且关于它的文档在示例方面有些缺乏。

我有一个类似于如下结构体:

#[derive(MyMacroHere)]
struct Example {
    id: i64,
    value: Option<String>,
}

我希望我的宏可以生成类似于下面的方法:

fn set_fields(&mut self, id: i64, value: Option<String>) {
    // ...
}

如何使用TokenStream特质来实现类似的东西的基本步骤是什么?


3
参考文献可能不是关于过程宏文档的最佳去处 - 你是否阅读了Rust书中的“宏”附录和内置库proc_macro的API文档?在我看来,这些将是最好的起点。 - Joe Clay
3
过程宏的基本思路是接收一个TokenStream(在本例中,就是组成Example定义的标记),然后运行一段代码以生成一个新的TokenStream添加到程序中(这个将会是set_fields定义所包含的标记)。人们通常使用syn库将输入标记转换为Rust语法树,使用quote库生成输出。 - Joe Clay
1个回答

52
  1. 为你的过程宏创建一个 crate:

    cargo new my_derive --lib
    
  2. 编辑 Cargo.toml 来使其成为一个过程宏 crate:

  3. [lib]
    proc-macro = true
    
  4. 实现你的过程宏:

    extern crate proc_macro;
    
    use proc_macro::TokenStream;
    
    #[proc_macro_derive(MyMacroHere)]
    pub fn my_macro_here_derive(input: TokenStream) -> TokenStream { 
        // ...
    }
    
  5. 导入过程宏并使用它:

  6. extern crate my_derive;
    
    use my_derive::MyMacroHere;
    
    #[derive(MyMacroHere)]
    struct Example {
        id: i64,
        value: Option<String>,
    }
    

难点在于宏的实现。大多数人使用 synquote crates 来解析输入的 Rust 代码,然后生成新的代码。

例如,syn 的文档从自定义派生的示例开始。您将解析结构体(或枚举类型或联合体),然后处理定义结构体的各种方式(单元、元组、命名字段)。您将收集所需的信息(类型,可能是名称),然后生成适当的代码。

另请参阅:


9
为了使解析更加容易,我构建了darling。它提供了一个类似于serde的API,您可以定义一个结构体来存储宏所需的参数,并将其传递给syn::DeriveInput。它处理解析、字段验证、重复字段检查和错误创建。 - ehdv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接