Я десериализирую конфигурационный файл YAML с помощью Serde. Для большинства структур, в которые я десериализуюсь, все довольно просто - между полями структур и свойствами в моем файле 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,
}
Чтобы Серде сделал для меня преобразование из MessageConfig
в Message
, я реализовал 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
имеет к чему-либо? И можно ли как-то заставить эту черту работать?