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

Rust中如何在声明式宏中接收结构体字段?

声明式宏接收结构体成员生成带公共成员的类型(结构体/枚举)

问题描述

我想通过声明式宏接收结构体成员,以此简便生成带有公共成员的枚举(因需配合serde反序列化预设JSON格式,我认为必须采用此方式),但尝试的代码无法运行。

尝试的代码

macro_rules! test {
    ($field_1:tt, $field_2:tt) => {
        #[derive(Debug)]
        struct Test {
            first: String,
            $field_1,
            $field_2,
        }
    };
}

test!(a: i32, b: i32);

报错信息

Compiling playground v0.0.1 (/playground)
error: no rules expected the token `:`
  --> src/lib.rs:12:8
   |
1  | macro_rules! test {
   | ----------------- when calling this macro
...
12 | test!(a: i32, b: i32);
   |        ^ no rules expected this token in macro call
   |
note: while trying to match `,`
  --> src/lib.rs:2:17
   |
2  |     ($field_1:tt, $field_2:tt) => {
   |                 ^

error: could not compile `playground` (lib) due to previous error

错误原因

使用tt(token tree)捕获器无法匹配a: i32这种完整的结构体成员——:会将其拆分为a、:、i32三个独立token tree,宏调用时第一个参数被解析为a,后续的:没有匹配规则,因此报错。

解决方案

不需要使用过程宏,声明式宏完全可以实现需求,核心是用片段捕获器匹配完整的结构体成员语法:

方案1:匹配标识符+类型的成员结构

通过明确匹配标识符: 类型的格式,支持任意数量的成员:

macro_rules! test {
    ($($ident:ident : $ty:ty),+) => {
        #[derive(Debug)]
        struct Test {
            first: String,
            $(
                $ident: $ty,
            )+
        }
    };
}

test!(a: i32, b: i32);

方案2:用struct_field片段(Rust 1.55+)

Rust 1.55及以上版本提供了专门的struct_field片段捕获器,可直接匹配完整的结构体成员(包括可选可见性、文档注释等):

macro_rules! test {
    ($($member:struct_field),+) => {
        #[derive(Debug)]
        struct Test {
            first: String,
            $(
                $member,
            )+
        }
    };
}

test!(a: i32, b: i32);
// 也支持带可见性的成员:test!(pub c: String, d: bool);

扩展:生成带公共成员的枚举(适配serde反序列化)

针对你提到的生成枚举并配合serde的需求,可编写如下宏:

use serde::Deserialize;

macro_rules! enum_with_common_fields {
    ($enum_name:ident, $($variant:ident { $($field:struct_field),* }),+) => {
        #[derive(Debug, Deserialize)]
        #[serde(tag = "type")] // 通过"type"字段区分枚举变体(可根据JSON格式调整)
        enum $enum_name {
            $(
                $variant {
                    // 公共成员
                    common_id: u64,
                    common_name: String,
                    $(
                        $field,
                    )*
                },
            )+
        }
    };
}

// 使用示例
enum_with_common_fields!(MyEnum,
    User { age: u32, email: String },
    Product { price: f64, stock: u32 }
);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 14:15:08