Примечание
Следующее не является хорошим долгосрочным решением, это просто обходной путь. Правильный способ сделать то, что вы хотите, - это найти и реализовать метод для согласования bincode
и serde_yaml
с erased_serde
. Но если вам нужно, чтобы оно работало прямо сейчас, вот
Суть
По сути, вы можете использовать enums для написания динамической отправки бедняку. Это выглядит примерно так (я упростил и опустил некоторые вещи):
struct JsonSerializer();
struct YamlSerializer();
trait Serializer {
fn serialize<V>(&self, thing: &V) -> ();
}
impl Serializer for JsonSerializer {
fn serialize<V>(&self, thing: &V) -> () {
println!("json");
}
}
impl Serializer for YamlSerializer {
fn serialize<V>(&self, thing: &V) -> () {
println!("yaml");
}
}
// That's what we'll be using instead of Box<dyn Serializer>
enum SomeSerializer {
Json(JsonSerializer),
Yaml(YamlSerializer),
}
impl SomeSerializer {
pub fn serialize<V>(&self, thing: &V) -> () {
match self {
SomeSerializer::Json(ser) => ser.serialize(thing),
SomeSerializer::Yaml(ser) => ser.serialize(thing),
}
}
}
Вот как вы можете его использовать (за исключением того, что вам, вероятно, понадобятся реальные функции конструктора):
pub fn main() {
let thing = 2;
let json = SomeSerializer::Json(JsonSerializer());
let yaml = SomeSerializer::Yaml(YamlSerializer());
json.serialize(&thing);
yaml.serialize(&yaml);
}
Это имеет серьезные недостатки (см. Ниже), но позволяет упаковывать что-то, имеющее универсальные методы, в унифицированный интерфейс.
проблемы
Основная проблема этого подхода заключается в том, что трудно добавить новые сериализаторы в настройку. С Box<dyn Serializer>
все, что вам нужно сделать, это impl Serializer
для чего-то. Здесь вы должны добавить вариант в enum и сопоставление с образцом во всех соответствующих методах. Это неудобно в ящике, где определено SomeSerializer
, а невозможно в других ящиках. Кроме того, добавление варианта к общедоступному перечислению является серьезным изменением, которое в нижних ящиках может не приветствоваться. Есть способы улучшить это до некоторой степени:
Скрыть SomeSerializer
На самом деле SomeSerializer
не имеет смысла быть публичным. Возможность сопоставления с шаблоном имеет очень мало преимуществ, и это общедоступность ограничивает то, что вы можете сделать с ним, не нарушая ситуацию ниже по течению. Обычное решение состоит в том, чтобы поместить его в непрозрачную структуру и экспортировать ее, оставив само перечисление скрытым:
pub struct VisibleSerializer(SomeSerializer);
Все еще используйте черту
Вы не можете расширить SomeSerializer
дополнительными сериализаторами в других ящиках. Вы можете продолжить монтировать на нем больше слоев enum (и это прискорбно и уродливо), но тогда ни одна функция в исходном ящике не примет такую конструкцию. В этом можно помочь: вместо того, чтобы делать serialize
неотъемлемым методом SomeSerializer
, внедрить для него Serializer
и сделать все функции, которые будут использовать SomeSerializer
универсальными, и принять T: Serializer
. Внезапно все нижестоящие ящики могут добавить желаемый сериализатор в настройку.
Особые случаи только особые случаи
Наличие более трех из четырех сериализаторов, упакованных таким образом, довольно смешно, не говоря уже о неловкости. Однако, если большинство сериализаторов, с которыми вы хотите работать, на самом деле erased_serde
-совместимы, вы можете иметь для них вариант всеобъемлющего перечисления в SomeSerializer
и иметь отдельные варианты только для несовместимых:
enum SomeSerializer {
Whatever(Box<dyn erased_serde::Serializer>),
}