代码之家  ›  专栏  ›  技术社区  ›  drpytho

如何创建可以使用Serde序列化并保留类型信息的泛型结构?

  •  4
  • drpytho  · 技术社区  · 6 年前

    我想使用Serde序列化通用的数据结构。

    my library的用户应该能够提供自己的结构来实现 Serialize Deserialize 。我应该能够获取他们序列化数据所用的值的原始类型信息。

    我该怎么做呢?

    我试过这样的方法:

    #[derive(Serialize, Deserialize)]
    struct Message<V> {
        key: Key,
        value: V,
    }
    

    我想找回 V 在我反序列化数据之后。

    是这样做的,还是我偏离了轨道?

    我希望用户能够扩展可能的值/类型。我想要枚举的行为,但是用户可以灵活地添加自己的结构作为可能的候选结构。类似 this code 但他们都需要独特的 ids

    2 回复  |  直到 6 年前
        1
  •  3
  •   Shepmaster Lukas Kalbertodt    5 年前

    我很确定这在一般意义上是完全不可能的。

    每种类型都能够控制自己的序列化。这意味着多个类型可能最终序列化为 相同的值 :

    #[derive(Debug, Deserialize, Serialize)]
    struct Age(i32);
    
    #[derive(Debug, Deserialize, Serialize)]
    struct Weight(i32);
    
    fn main() {
        let a = Age(42);
        let a_str = serde_json::to_string(&a).unwrap();
        println!("{}", a_str); // 42
        let b: Weight = serde_json::from_str(&a_str).unwrap();
        println!("{:?}", b);
    }
    

    您可以尝试在序列化数据中建立标识类型的标记,但最终会出现相同的问题:始终存在冲突的可能性。

    更根本的是,这样的概念在Rust中是无效的,因为每个类型在编译时都必须有一个已知的大小。无法反序列化为变量 未知大小

    同样重要的是,您会为泛型 V ?在编译代码时,不能“忘记”指定所有泛型类型。

    正因为如此,通常这样的事情都不会发生。如果用户使用具体类型调用序列化代码,则可以使用相同的具体类型调用反序列化代码。

    我想要枚举的行为,但需要用户添加自己的结构的灵活性。

    听起来像是 特征对象

    另请参见:

        2
  •  0
  •   Julian Lazaras    4 年前

    实现这种想法的最佳方法是限制V的类型,然后使用结构名称作为标识模块类型+字符串的方法。

    实例

    pub struct Message<V: Serialize + Deserialize> {
         key: keytype,
         value: V,
         crate_path: String
    }
    

    然后使用类似sha的方法为结构名称生成哈希,反序列化以检查结构名称。