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

如何实现泛型枚举的“Serde::Deserialize”特性

  •  0
  • lucatrv  · 技术社区  · 1 年前

    让我们考虑以下示例通用枚举:

    enum Enum<T, U> {
        T(T),
        U(U),
    }
    

    实施 Serde::Serialize 的特征 Enum 相当容易:

    impl<T, U> Serialize for Enum<T, U>
    where
        T: Serialize,
        U: Serialize,
    {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
        {
            match self {
                Enum::T(t) => t.serialize(serializer),
                Enum::U(u) => u.serialize(serializer),
            }
        }
    }
    

    然而,我很难实现 Serde::Deserialize 性状,其表现应如下:

    • 如果值可以反序列化为类型 T ,那么它应该会返回 Enum::T(t) .
    • 否则,如果值可以反序列化为类型 U ,那么它应该会返回 Enum::U(u) .
    • 否则,它应该返回一个错误。

    这是我能接近的:

    impl<'de, T, U> Deserialize<'de> for Enum<T, U>
    where
        T: Deserialize<'de>,
        U: Deserialize<'de>,
    {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: Deserializer<'de>,
        {
            if let Ok(t) = T::deserialize(deserializer) {
                Ok(Enum::T(t))
            } else {
                if let Ok(u) = U::deserialize(deserializer) {
                    Ok(Enum::U(u))
                } else {
                    Err(D::Error::custom("Failed to deserialize Enum"))
                }
            }
        }
    }
    

    但是,我得到以下错误:

    error[E0382]: use of moved value: `deserializer`
       --> src\lib.rs:731:47
        |
    724 |         fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        |                           ------------ move occurs because `deserializer` has type `D`, which does not implement the `Copy` trait
    ...
    728 |             if let Ok(t) = T::deserialize(deserializer) {
        |                                           ------------ value moved here
    ...
    731 |                 if let Ok(f) = F::deserialize(deserializer) {
        |                                               ^^^^^^^^^^^^ value used here after move
        |
    help: consider further restricting this bound
        |
    726 |             D: Deserializer<'de> + std::marker::Copy,
        |                                  +++++++++++++++++++
    
    For more information about this error, try `rustc --explain E0382`.
    

    我知道发生了什么,但我不确定如何修复它(编译器的建议也不起作用)。

    0 回复  |  直到 1 年前
        1
  •  0
  •   lucatrv    1 年前

    在Kevin Reid发表评论后,我使用 cargo expand 查看生成的代码 serde_derive 对于未标记的枚举,并提出了以下实现:

    impl<'de, T, U> Deserialize<'de> for Enum<T, U>
    where
        T: Deserialize<'de>,
        F: Deserialize<'de>,
    {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: Deserializer<'de>,
        {
            let content = serde::__private::de::Content::deserialize(de)?;
            let deserializer = serde::__private::de::ContentRefDeserializer::<D::Error>::new(
            &content,
        );
            if let Ok(t) = T::deserialize(de) {
                Ok(Enum::T(t))
            } else if let Ok(u) = U::deserialize(de) {
                Ok(Enum::U(u))
            } else {
                Err(D::Error::custom("input data did not match any variant type"))
        }
    }
    

    诀窍在于依靠 serde::__private::de::Content serde::__private::de::ContentRefDeserializer ,目前在Serde私有API中,但未来可能会公开,请参见:

    推荐文章