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
相关产品推荐
相关产品推荐

