Rust中如何将互斥分析结构体扁平化合并为Analysis结构体?
Rust 实现带扁平化专属字段的强类型 Analysis 结构体方案
核心思路
要实现类似 TypeScript 联合类型的强绑定效果,同时让专属字段扁平化到顶层(解决 OpenAPI 文档嵌套问题),我们可以通过带固定类型标识的子结构体 + 无标签枚举的组合方案实现,既保证类型严格匹配,又满足序列化/文档的扁平化需求。
1. 依赖准备
确保 Cargo.toml 中包含必要依赖:
[dependencies] serde = { version = "1.0", features = ["derive"] } chrono = { version = "0.4", features = ["serde", "utc"] } schemars = { version = "0.8", features = ["chrono"] } # 用于生成符合规范的 OpenAPI 文档
2. 代码实现
use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; // 定义分析类型的固定枚举,序列化时自动转为蛇形命名 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)] #[serde(rename_all = "snake_case")] pub enum AnalysisType { Chemical, Physical, Biological, } // 用宏提取公共字段,避免重复代码 macro_rules! analysis_common_fields { () => { pub id: String, pub name: String, pub created: DateTime<Utc>, pub modified: DateTime<Utc>, }; } // 化学分析结构体:公共字段 + 专属字段 + 固定类型标识 #[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)] pub struct ChemicalAnalysis { analysis_common_fields!(), #[serde(rename = "analysis_type")] pub analysis_type: AnalysisType, pub ph_level: f32, pub chemical_composition: String, } // 物理分析结构体 #[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)] pub struct PhysicalAnalysis { analysis_common_fields!(), #[serde(rename = "analysis_type")] pub analysis_type: AnalysisType, pub temperature: f32, pub pressure: f32, } // 生物分析结构体 #[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)] pub struct BiologicalAnalysis { analysis_common_fields!(), #[serde(rename = "analysis_type")] pub analysis_type: AnalysisType, pub organism_type: String, pub cell_count: u32, } // 无标签枚举作为联合类型,serde 会自动根据字段匹配对应变体 #[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)] #[serde(untagged)] pub enum Analysis { Chemical(ChemicalAnalysis), Physical(PhysicalAnalysis), Biological(BiologicalAnalysis), } // 为子结构体添加构造函数,强制绑定固定的 analysis_type impl ChemicalAnalysis { pub fn new( id: String, name: String, created: DateTime<Utc>, modified: DateTime<Utc>, ph_level: f32, chemical_composition: String, ) -> Self { Self { id, name, created, modified, analysis_type: AnalysisType::Chemical, ph_level, chemical_composition, } } } // 同理为 PhysicalAnalysis、BiologicalAnalysis 添加构造函数
3. 方案优势
- 字段扁平化:序列化后所有字段(公共、类型标识、专属)都在顶层,不会出现枚举变体的嵌套键,完全符合 OpenAPI 文档的扁平化需求。
- 类型严格匹配:通过构造函数强制每个子结构体的
analysis_type与自身类型绑定,不会出现类型不匹配的情况,实现类似 TypeScript 联合类型的强约束。 - 自动序列化/反序列化:无标签枚举让 serde 自动根据
analysis_type值匹配对应变体,反序列化时字段不匹配会直接返回错误,保证数据安全。 - 标准 OpenAPI 文档:schemars 会自动生成
oneOf类型的 schema,前端看到的结构与 TypeScript 联合类型完全一致。
4. 序列化示例
fn main() { let chemical = ChemicalAnalysis::new( "test-123".to_string(), "饮用水质检测".to_string(), Utc::now(), Utc::now(), 7.2, "H2O, NaCl".to_string(), ); let analysis = Analysis::Chemical(chemical); let json = serde_json::to_string_pretty(&analysis).unwrap(); println!("{}", json); }
输出的 JSON 完全扁平化:
{ "id": "test-123", "name": "饮用水质检测", "created": "2024-05-20T14:30:00Z", "modified": "2024-05-20T14:30:00Z", "analysis_type": "chemical", "ph_level": 7.2, "chemical_composition": "H2O, NaCl" }
内容的提问来源于stack exchange,提问作者Seth Lutske
相关产品推荐
相关产品推荐

