如何在Rust中的嵌套结构中添加泛型类型?

wrrgggsh  于 2022-12-23  发布在  其他
关注(0)|答案(1)|浏览(160)

我知道有一个类似的问题here,但我还不能使它适合我的用例。
我有一个嵌套在其他结构体中的Model结构体。模型可以有两种不同类型的Config对象,ModelConfig或SeedConfig。除了几个字段之外,它们几乎是相同的。目前,我需要Model的两个具体实现(SeedModel和ModelModel)来更改config字段,这导致了所有方法和trait实现的重复。

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct MetaModel {
   pub model: Model
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Model {
    pub name: String,
    pub config: Option<ModelConfig>
}

我尝试过的:

  • 使用泛型:这将泛型类型推到链的上方,并导致非常复杂的定义和区域,在这些区域中我没有上下文来创建父结构(即,MetaModel定义在创建时无法访问Model定义)。

这最终会导致the type parameter C is not constrained by the impl trait, self type, or predicates unconstrained type parameter错误

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct MetaModel<C> {
   pub model: Model<C>
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Model<C> {
    pub name: String,
    pub config: Option<C>
}
  • 特征对象:这不起作用,因为serde不能序列化trait对象
pub trait Config {}

pub struct ModelConfig;
impl Config for ModelConfig {}
pub struct SeedConfig;
impl Config for SeedConfig {}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Model {
    pub name: String,
    pub config: Option<Box<dyn Config>
}

我想做的是:

impl OtherTrait for Model {
    type Value = Model;
    fn build_model(&self, m: DerivedMeta) -> Result<Self::Value, &'static str> {
        Ok(Model {
           // Either a SeedConfig or a ModelConfig
        })
    }
}
lsmepo6l

lsmepo6l1#

我将使用#[serde(flatten)]#[serde(untagged)]的组合:

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
struct Config {
    members: i32,
    shared_by: String,
    both: i64,

    #[serde(flatten)]
    specific: SpecificConfig,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(untagged)]
enum SpecificConfig {
    SeedConfig {
        some_members: i16,
        unique_to: String,
        seed_config: u64,
    },
    ModelConfig {
        other_members: i8,
        not_shared_with_above: u32,
    },
}

flatten的序列解释:
将此字段的内容平铺到定义它的容器中。
这将删除序列化表示和Rust数据结构表示之间的一个结构级别。
关于untagged的解释:
没有明确的标记来标识数据包含的变量,Serde将尝试按顺序将数据与每个变量进行匹配,第一个反序列化成功的变量将被返回。
通过将这两种行为结合起来,我们可以得到以下行为:

  • flatten允许所有共享字段和特定字段在配置中处于同一级别
  • untagged允许我们避免在配置中添加显式标记
  • 可直接访问所有共享属性
  • 只有特定属性需要匹配specific枚举

相关问题