You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Rust crate、模块或函数生成代码?#[derive]不可用

实现模块级代码生成:用属性宏替代derive宏

你说得对,标准的#[derive]属性确实只能作用在结构体、枚举或者联合类型上,没法直接用于模块、crate或者函数。要实现你想要的「给模块打标记,自动生成调用模块内多个项的聚合函数」这个需求,我们得换个思路——用**Rust属性宏(Attribute Macro)**来替代derive宏,因为属性宏的适用范围更广,可以作用在模块、函数等多种代码项上。

下面是具体的实现步骤:

1. 创建过程宏 crate

首先需要新建一个专门的过程宏 crate,因为自定义宏需要依赖syn和quote这类解析和生成Rust代码的库。

Cargo.toml 配置

[package]
name = "my_agg_macro"
version = "0.1.0"
edition = "2021"

[lib]
proc-macro = true

[dependencies]
syn = { version = "2.0", features = ["full", "parsing"] }
quote = "1.0"
proc-macro2 = "1.0"

2. 编写属性宏逻辑

我们的宏需要完成这几件事:

  • 解析输入的模块内容
  • 遍历模块内的结构体和函数,收集它们的标识符
  • 生成一个聚合函数,依次调用/实例化这些项

宏的代码实现:

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemMod};

#[proc_macro_attribute]
pub fn my_agg(_attr: TokenStream, input: TokenStream) -> TokenStream {
    // 解析传入的模块代码
    let module = parse_macro_input!(input as ItemMod);
    let module_name = module.ident.clone();

    // 收集需要生成调用代码的项
    let mut call_blocks = Vec::new();

    // 提取模块内部的代码项(如果模块是inline的)
    if let Some((_, items)) = module.content {
        for item in items {
            match item {
                // 处理结构体:生成实例化并打印的代码
                syn::Item::Struct(struct_item) => {
                    let struct_name = struct_item.ident;
                    call_blocks.push(quote! {
                        println!("{{:?}}", #module_name::#struct_name {});
                    });
                }
                // 处理函数:生成调用函数的代码
                syn::Item::Fn(fn_item) => {
                    let fn_name = fn_item.sig.ident;
                    call_blocks.push(quote! {
                        #module_name::#fn_name();
                    });
                }
                // 如果需要支持枚举或其他类型,可以在这里扩展逻辑
                _ => {}
            }
        }
    }

    // 生成最终的代码:保留原模块 + 新增聚合函数
    let expanded_code = quote! {
        // 保留用户定义的原模块
        #module

        // 自动生成的聚合函数
        fn example1() {
            #(#call_blocks)*
        }
    };

    // 将生成的代码转换为TokenStream返回
    expanded_code.into()
}

3. 在主项目中使用宏

回到你的主项目,先在Cargo.toml中添加对宏 crate 的依赖:

[dependencies]
my_agg_macro = { path = "./my_agg_macro" }

然后就可以像你期望的那样使用宏了(注意要给结构体加上#[derive(Debug)],因为我们用了{:?}格式化输出):

use my_agg_macro::my_agg;

#[my_agg]
mod AAA {
    #[derive(Debug)]
    struct BBB {}
    #[derive(Debug)]
    struct CCC {}
    fn ddd() {
        println!("Executing AAA::ddd()");
    }
}

fn main() {
    // 调用自动生成的聚合函数
    example1();
}

运行这段代码,你会看到输出:

BBB
CCC
Executing AAA::ddd()

关键说明

  • 为什么不用#[derive]:Rust的#[derive]宏是专门为数据结构(结构体、枚举、联合)设计的,它的输入类型DeriveInput只能解析这些类型,无法处理模块。而属性宏#[proc_macro_attribute]可以接收任意代码项作为输入,包括模块,这正好匹配你的需求。
  • 扩展空间:你可以轻松扩展这个宏的功能,比如:
    • 允许通过宏参数自定义生成的聚合函数名称(比如#[my_agg(fn_name = "my_custom_agg")])
    • 只处理带有特定标记的项(比如给需要被聚合的结构体加上#[include_in_agg]属性)
    • 支持嵌套模块的解析
    • 自定义生成的调用逻辑(比如不打印,而是收集实例到一个 Vec 中)

内容的提问来源于stack exchange,提问作者Cage

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:54:15