如何为实现特定特征的所有类型批量实现反序列化?

时间:2019-01-02 21:00:53

标签: rust deserialization traits serde

我正在用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的转换,我为Deserialize实现了Message

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与什么有什么关系?并可以通过某种方式使该特征起作用?

1 个答案:

答案 0 :(得分: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(())
}