Как я могу массово внедрить десериализацию для всех типов, которые реализуют определенную черту? - PullRequest
0 голосов
/ 03 января 2019

Я десериализирую конфигурационный файл 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 имеет к чему-либо? И можно ли как-то заставить эту черту работать?

1 Ответ

0 голосов
/ 03 января 2019

Я не уверен, есть ли способ определить такую ​​широкую черту, не вызывая противоречивых реализаций.Что вы можете сделать, это использовать макрос, чтобы избежать повторений:

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(())
}
...