【发布时间】: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 为我完成从 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))
}
}
这行得通,但是如果我要为每个结构都这样做,就会涉及到大量的反序列化代码的复制粘贴,所以我想我应该从中做出一个特征:
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
DeserializeforBox<T>。您的实现涵盖T,它可能是Box<T>。两个 crate 的用户都可以构造一个可以反序列化为您的代码或 Serde 中的代码的类型。这是不允许的。 -
@Shepmaster 也许我在这里犯了一个基本错误。我认为通过指定
where T: Configurable<Config = C>我对Deserialize的实现将只涵盖Configurable的实现者。Box<T>不是Configurable的实现者,所以我认为不会有问题。 -
当
MyOwnType也实现Deserialize时,是什么阻止了某人为Box<MyOwnType>实现Configurable?
标签: rust deserialization traits serde