Я десериализую файл конфигурации 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
? И можно ли как-нибудь заставить эту черту работать?
Deserialize
? Если они не одинаковы, тогда какова польза от добавления дополнительной косвенности? - person Shepmaster   schedule 03.01.2019Deserialize
дляBox<T>
. Ваша реализация охватываетT
, который может бытьBox<T>
. Пользователь обоих ящиков может создать тип, который можно десериализовать либо как ваш код, либо как код в Serde. Это запрещено. - person Shepmaster   schedule 03.01.2019where T: Configurable<Config = C>
, моя реализацияDeserialize
будет охватывать только разработчиковConfigurable
.Box<T>
не является разработчикомConfigurable
, поэтому я решил, что проблем не возникнет. - person Anders   schedule 03.01.2019Configurable
дляBox<MyOwnType>
, когдаMyOwnType
также реализуетDeserialize
? - person Shepmaster   schedule 03.01.2019