如何为实现特定特征的所有类型批量实现反序列化?
How can I mass implement Deserialize for all types that implement a specific trait?
我正在使用 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
和什么有什么关系?是否有可能使这种特性发挥作用?
我不确定是否有一种方法可以在不导致实现冲突的情况下定义如此广泛的特征。你可以做的是使用宏来避免重复:
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(())
}
我正在使用 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
和什么有什么关系?是否有可能使这种特性发挥作用?
我不确定是否有一种方法可以在不导致实现冲突的情况下定义如此广泛的特征。你可以做的是使用宏来避免重复:
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(())
}