如何在Rust的不同结构体定义中添加公共字段?
为Rust结构体通过自定义属性宏添加公共字段
需求背景
现有两个Rust结构体定义:
#[derive(Default, Serialize, Deserialize)] pub struct User { pub id: String, pub username: String, #[serde(rename(serialize = "accountGroupId", deserialize = "accountGroupId"))] pub account_group_id: String, } #[derive(Default, Serialize, Deserialize)] pub struct Client { pub id: String, pub contact: String, }
需要为这两个结构体添加enable公共字段,实现直接通过user.enable、client.enable访问的使用方式:
let user = User { id: "user-id-value".to_string(), enable: true, ..Default::default() }; let client = Client { id: "client-id-value".to_string(), enable: true, ..Default::default() };
要求:
- 不使用trait(无法直接字段访问)
- 不使用嵌套字段(避免
user.base.enable这类层级访问) - 通过自定义属性宏
#[user_base]实现自动添加公共字段
实现方案
要实现这个需求,需要基于Rust的**过程宏(proc-macro)**编写自定义属性宏,具体步骤如下:
1. 创建过程宏 crate
新建一个proc-macro类型的crate(比如命名为base_fields_macro),在Cargo.toml中配置:
[package] name = "base_fields_macro" version = "0.1.0" edition = "2021" [lib] proc-macro = true [dependencies] syn = { version = "2.0", features = ["full", "extra-traits"] } quote = "1.0"
2. 实现#[user_base]属性宏
在src/lib.rs中编写宏逻辑,解析输入的结构体AST并插入enable字段:
use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, DeriveInput, Fields}; #[proc_macro_attribute] pub fn user_base(_attr: TokenStream, input: TokenStream) -> TokenStream { // 解析输入的结构体定义 let mut input = parse_macro_input!(input as DeriveInput); // 仅支持带命名字段的结构体 if let syn::Data::Struct(ref mut data_struct) = input.data { match &mut data_struct.fields { Fields::Named(fields_named) => { // 插入enable字段,添加serde默认值处理 let enable_field = syn::parse_quote! { #[serde(default)] pub enable: bool, }; fields_named.named.push(enable_field); } Fields::Unnamed(_) | Fields::Unit => { panic!("#[user_base] only supports structs with named fields"); } } } else { panic!("#[user_base] can only be applied to structs"); } // 生成修改后的结构体代码 let expanded = quote! { #input }; expanded.into() }
3. 在主项目中使用宏
在主项目的Cargo.toml中引入过程宏依赖:
[dependencies] base_fields_macro = { path = "./base_fields_macro" } serde = { version = "1.0", features = ["derive"] }
给目标结构体添加#[user_base]属性即可:
use base_fields_macro::user_base; use serde::{Deserialize, Serialize}; #[user_base] #[derive(Default, Serialize, Deserialize)] pub struct User { pub id: String, pub username: String, #[serde(rename(serialize = "accountGroupId", deserialize = "accountGroupId"))] pub account_group_id: String, } #[user_base] #[derive(Default, Serialize, Deserialize)] pub struct Client { pub id: String, pub contact: String, } fn main() { let user = User { id: "user-id-value".to_string(), enable: true, ..Default::default() }; let client = Client { id: "client-id-value".to_string(), enable: true, ..Default::default() }; println!("User enable status: {}", user.enable); println!("Client enable status: {}", client.enable); }
说明
- 宏会自动在结构体的命名字段中插入
pub enable: bool字段,并添加#[serde(default)]确保反序列化时缺失该字段会使用默认值false - 由于结构体派生了
Default,创建实例时可以通过..Default::default()补全未指定的字段,包括enable的默认值false - 该宏仅支持带命名字段的结构体,若应用于其他类型会触发编译 panic
内容的提问来源于stack exchange,提问作者Hui
相关产品推荐
相关产品推荐

