[英]How can I mass implement Deserialize for all types that implement a specific trait?
I am deserializing a YAML config file with Serde.我正在使用 Serde 反序列化 YAML 配置文件。 For most structs I deserialize into, things are quite simple — there's a one-to-one relationship between the fields of the structs and the properties in my YAML file.
对于我反序列化的大多数结构,事情非常简单——结构的字段和我的 YAML 文件中的属性之间存在一对一的关系。
In a few cases, things are a bit more complicated.在少数情况下,事情会更复杂一些。 For these, the properties in the YAML file are better viewed as parameters to the constructor.
对于这些,最好将 YAML 文件中的属性视为构造函数的参数。 The actual struct will have different fields, calculated from those.
实际的结构将有不同的字段,根据这些字段计算。
For these cases, I have written separate config structs that I deserialize into.对于这些情况,我编写了反序列化的单独配置结构。 For simplicity, consider this silly example:
为简单起见,考虑这个愚蠢的例子:
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,
}
To have Serde do the conversion from MessageConfig
to Message
for me, I implemented Deserialize
for Message
:为了让 Serde 为我完成从
MessageConfig
到Message
的转换,我为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))
}
}
This works, but there would be a lot of copy pasting of the deserialization code involved if I were to do this for every struct, so I figured I should make a trait out of it:这是可行的,但是如果我要为每个结构执行此操作,则会涉及大量反序列化代码的复制粘贴,因此我想我应该从中创建一个特征:
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,
}
However, the compiler is not happy about this:但是,编译器对此并不满意:
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
The error messages make little sense to me.错误消息对我来说毫无意义。 What does
Box
have to do with anything? Box
和什么有什么关系? And is it somehow possible to make this trait work?是否有可能使这种特性发挥作用?
I am not sure if there's a way to define such a broad trait without causing conflicting implementations.我不确定是否有一种方法可以定义如此广泛的特征而不会导致实现冲突。 What you could do is use a macro to avoid repetition:
您可以做的是使用宏来避免重复:
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(())
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.