【问题标题】:How can I mass implement Deserialize for all types that implement a specific trait?如何为实现特定特征的所有类型大规模实现反序列化?
【发布时间】:2019-01-02 21:00:53
【问题描述】:

我正在使用 Serde 反序列化 YAML 配置文件。对于我反序列化的大多数结构,事情都非常简单——结构的字段和我的 YAML 文件中的属性之间存在一对一的关系。

在某些情况下,事情会稍微复杂一些。对于这些,YAML 文件中的属性最好被视为构造函数的参数。实际结构将具有不同的字段,根据这些字段计算得出。

对于这些情况,我编写了单独的配置结构,并将其反序列化为。为简单起见,考虑这个愚蠢的例子:

struct Message {
    text: String,
}

impl Message {
    fn from_config(config: MessageConfig) -> Message {
        Message {
            text: format!("{} {}", config.first_half, config.second_half),
        }
    }
}

#[derive(Deserialize)]
struct MessageConfig {
    first_half: String,
    second_half: String,
}

为了让 Serde 为我完成从 MessageConfigMessage 的转换,我为 Message 实现了 Deserialize

impl<'de> Deserialize<'de> for Message {
    fn deserialize<D>(deserializer: D) -> Result<Message, D::Error>
    where
        D: Deserializer<'de>,
    {
        MessageConfig::deserialize(deserializer).map(|config| Message::from_config(config))
    }
}

这行得通,但是如果我要为每个结构都这样做,就会涉及到大量的反序列化代码的复制粘贴,所以我想我应该从中做出一个特征:

use serde::{Deserialize, Deserializer};
use serde_json;
#[macro_use]
extern crate serde_derive;

trait Configurable {
    type Config;
    fn from_config(config: Self::Config) -> Self;
}

impl<'de, T, C> Deserialize<'de> for T
where
    T: Configurable<Config = C>,
    C: Deserialize<'de>,
{
    fn deserialize<D>(deserializer: D) -> Result<T, D::Error>
    where
        D: Deserializer<'de>,
    {
        Self::Config::deserialize(deserializer).map(|config| Self::from_config(config))
    }
}

struct Message {
    text: String,
}

impl<'de> Configurable for Message {
    type Config = MessageConfig;

    fn from_config(config: MessageConfig) -> Message {
        Message {
            text: format!("{} {}", config.first_half, config.second_half),
        }
    }
}

#[derive(Deserialize)]
struct MessageConfig {
    first_half: String,
    second_half: String,
}

但是,编译器对此并不满意:

error[E0119]: conflicting implementations of trait `_IMPL_DESERIALIZE_FOR_MessageConfig::_serde::Deserialize<'_>` for type `std::boxed::Box<_>`:
  --> src/lib.rs:11:1
   |
11 | / impl<'de, T, C> Deserialize<'de> for T
12 | | where
13 | |     T: Configurable<Config = C>,
14 | |     C: Deserialize<'de>,
...  |
21 | |     }
22 | | }
   | |_^
   |
   = note: conflicting implementation in crate `serde`:
           - impl<'de, T> _IMPL_DESERIALIZE_FOR_MessageConfig::_serde::Deserialize<'de> for std::boxed::Box<T>
             where T: _IMPL_DESERIALIZE_FOR_MessageConfig::_serde::Deserialize<'de>;
   = note: downstream crates may implement trait `Configurable` for type `std::boxed::Box<_>`

error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g. `MyStruct<T>`)
  --> src/lib.rs:11:1
   |
11 | / impl<'de, T, C> Deserialize<'de> for T
12 | | where
13 | |     T: Configurable<Config = C>,
14 | |     C: Deserialize<'de>,
...  |
21 | |     }
22 | | }
   | |_^ type parameter `T` must be used as the type parameter for some local type
   |
   = note: only traits defined in the current crate can be implemented for a type parameter

错误消息对我来说毫无意义。 Box 和什么有什么关系?是否有可能使这种特性发挥作用?

【问题讨论】:

  • @Shepmaster 我不认为这是重复的。建议的重复:结构具有引用特征的字段。如何反序列化?这个问题:我想为所有实现特定特征的类型大规模实现反序列化。
  • 如果您的所有结构都具有相同的反序列化实现,那么为什么不让它们成为相同的实际结构(或都包含相同的结构)并为此实现Deserialize?如果它们不相同,那么添加额外的间接性有什么好处?
  • 这个错误比较直接。塞尔德implements Deserialize for Box&lt;T&gt;。您的实现涵盖T,它可能是Box&lt;T&gt;。两个 crate 的用户都可以构造一个可以反序列化为您的代码或 Serde 中的代码的类型。这是不允许的。
  • @Shepmaster 也许我在这里犯了一个基本错误。我认为通过指定where T: Configurable&lt;Config = C&gt; 我对Deserialize 的实现将只涵盖Configurable 的实现者。 Box&lt;T&gt; 不是 Configurable 的实现者,所以我认为不会有问题。
  • MyOwnType 也实现Deserialize 时,是什么阻止了某人为Box&lt;MyOwnType&gt; 实现Configurable

标签: rust deserialization traits serde


【解决方案1】:

我不确定是否有一种方法可以定义如此广泛的特征而不会导致实现冲突。您可以做的是使用宏来避免重复:

use serde::{Deserialize, Deserializer};
use serde_json;

use serde_json::Error;

#[macro_use]
extern crate serde_derive;

struct Message {
    text: String,
}

#[derive(Deserialize)]
struct MessageConfig {
    first_half: String,
    second_half: String,
}

impl Message {
    fn from_config(config: MessageConfig) -> Message {
        Message {
            text: format!("{} {}", config.first_half, config.second_half),
        }
    }
}

macro_rules! derive_configurable_serializer {
    ( $t:ident, $c:ident ) => {
        impl<'de> Deserialize<'de> for $t {
            fn deserialize<D>(deserializer: D) -> Result<$t, D::Error>
            where
                D: Deserializer<'de>,
            {
                $c::deserialize(deserializer).map(|config| $t::from_config(config))
            }
        }
    };
}

derive_configurable_serializer!(Message, MessageConfig);

fn main() -> Result<(), Error> {
    let data = r#"{ "first_half": "John", "second_half": "Doe" }"#;

    let p: Message = serde_json::from_str(data)?;

    println!("Hello, {}!", p.text);

    Ok(())
}

【讨论】:

  • 宏的好主意,感谢您展示如何做到这一点!不过,我不明白冲突的实现是怎么回事。没有任何类型可以同时从其他来源实现 Configurable 和 Deserialize。
  • @Anders 老实说-我不知道。我尝试找到具有该特征的解决方案,但收到了各种错误消息。也许对 Rust 特征有更深入了解的人可以解释如何做到这一点/为什么不可能。
猜你喜欢
  • 2019-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-07
相关资源
最近更新 更多