在 rust 中将非此即彼结构扁平化为另一个结构

Flattening either-or structs into another struct in rust

提问人:Seth Lutske 提问时间:11/9/2023 更新时间:11/9/2023 访问量:47

问:

我正在尝试在 rust 中创建一个灵活但特定类型的结构。我们称之为 struct 。所有结构都必须具有字段 、 、 、 。但有 3 种不同类型的分析:AnalysisAnalysisnameidcreatedmodified

// all enums and struct use the #[derive(Serialize, Deserialize, ToSchema)] macro

pub enum AnalysisTypes {
    Chemical,
    Physical,
    Biological,
}

每种特定的分析类型都有其自己的特定字段:

pub struct ChemicalAnalysis {
    statistics: Vec<Statistic>, // doesn't matter what this is exactly
    method_parameters: MethodParameters,
}

pub struct PhysicalAnalysis {
    statistics: Vec<Statistic>,
}

pub struct BiologicalAnalysis {
    sampling_information: Vec<SampleInformation>,
}

我的目标是创建一个 struct ,它具有上述所有常见属性,以及来自上述分析结构之一的所有属性。因此,我创建了一个枚举,它可以是其中任何一个,然后将其包含在我的结构中:Analysis

pub enum AnalysisEnum {
    Chemical(ChemicalAnalysis),
    Physical(PhysicalAnalysis),
    Biological(BiologicalAnalysis),
}

pub struct Analysis {
    name: String,
    id: Uuid,
    created: DateTime<Utc>,
    modified: DateTime<Utc>,
    #[serde(flatten)]     // <------------ flatten values into struct here
    more_fields: AnalysisEnum,
}

这几乎得到了我想要的东西。当我在 OpenAPI 文档中检查这一点时,分析结构将始终具有“通用”字段,并且需要一个名称为“化学”、“物理”或“生物”的条目,每个条目都有各自的字段。serde(flatten)

enter image description here

我想“de-shell”这些属性,并将它们直接展平到父结构中,而不是要求它们位于命名属性下。我该怎么做?

此外,我如何要求父结构中的属性,该属性的类型为 AnalysisEnum,专门对应于每个 AnalysisEnum?例如:Analysisanalysis_type

pub enum AnalysisTypes {
    Chemical,
    Physical,
    Biological,
}

pub struct ChemicalAnalysis {
    analysis_type: AnalysisTypes::Chemical, // this is not correct syntax, errors
    statistics: Vec<Statistic>,
    method_parameters: MethodParameters,
}

pub struct PhysicalAnalysis {
    analysis_type: AnalysisTypes::Physical, // this is not correct syntax, errors
    statistics: Vec<Statistic>,
}

...

如果这是一个简单的问题,或者我想要的设计模式不符合语言的精神,请原谅我。我来自打字稿背景,可以像这样轻松完成:

enum AnalysisTypes {
    Chemical = "Chemical",
    Physical = "Physical",
    Biological = "Biological",
}

interface ChemicalAnalysis {
    analysis_type: AnalysisTypes.Chemical,
    statistics: Statistic[],
    method_parameters: MethodParameters,
}

interface PhysicalAnalysis {
    analysis_type: AnalysisTypes.Physical,
    statistics:Statistic[],
}

interface BiologicalAnalysis {
    analysis_type: AnalysisTypes.Biological,
    sampling_information:SampleInformation[],
}

type Analysis = {
    name: String;
    id: Uuid;
    created: DateTime<Utc>;
    modified: DateTime<Utc>;
} & (ChemicalAnalysis | PhysicalAnalysis | BiologicalAnalysis)
rust struct 枚举 flatten serde

评论

0赞 drewtato 11/9/2023
听起来你想要一个内部标记的枚举: serde.rs/enum-representations.html 我也认为你应该去掉这个枚举。AnalysisTypes
0赞 Seth Lutske 11/9/2023
@drewtato这是一个宾果游戏。正是我想要的!作为一个生锈的新手,我以前从未听说过,但这正是我所需要的。随意写一个答案,这样我就可以给你点赞

答:

0赞 Miiao 11/9/2023 #1

当可以制作一个枚举时,为什么要制作五种不同的类型(、、、、)、?AnalysisEnumAnalysisTypeChemicalAnalysisPhysicalAnalysisBiologicalAnalysis

pub enum AnalysisType {
    Chemical {
        statistics: Vec<Statistic>,
        method_parameters: MethodParameters,
    },
    Physical {
        statistics: Vec<Statistic>,
    },
    Biological {
        sampling_information: Vec<SampleInformation>,
    },
}

pub struct Analysis {
    name: String,
    id: Uuid,
    created: DateTime<Utc>,
    modified: DateTime<Utc>,
    spec: AnalysisType,
}

我想“de-shell”这些属性,并将它们直接展平到父结构中,而不是要求它们位于命名属性下。我该怎么做?

不。大小类型是大小类型,它不能根据运行时值进行更改。你必须把事情说得很清楚。不过,我可能错了你真正想要的。


塞尔德

也许你只是想要?tag = "type"


我想要的设计模式...

感觉是这样。

3赞 Yoric 11/9/2023 #2

您要查找的内容称为(您可以将“analysis_type”)]“替换为要用于存储标记的字段的名称)。#[serde(tag = "analysis_type")]

#[derive(Serialize, Deserialize)]
#[serde(tag = "analysis_type")]
pub enum AnalysisEnum {
    Chemical(ChemicalAnalysis),
    Physical(PhysicalAnalysis),
    Biological(BiologicalAnalysis),
}

#[derive(Serialize, Deserialize)]
pub struct Analysis {
    name: String,
    id: Uuid,
    created: DateTime<Utc>,
    modified: DateTime<Utc>,
    #[serde(flatten)]
    more_fields: AnalysisEnum,
}

(操场]

有关如何在 Serde 中表示枚举的完整参考,请参阅 https://serde.rs/enum-representations.html