Как вообще можно добавить десериализацию объектов полиморфных признаков в Rust?

Я пытаюсь решить проблему сериализации и десериализации Box<SomeTrait>, Я знаю, что в случае иерархии закрытого типа, рекомендуемый способ - использовать enum, и нет проблем с их сериализацией, но в моем случае использование enums - неуместное решение.

Сначала я попытался использовать Serde, поскольку это де-факто механизм сериализации Rust. Серде способен на сериализацию Box<X> но не в том случае, когда X это черта. Serialize trait не может быть реализован для объектов trait, потому что он имеет универсальные методы. Эта конкретная проблема может быть решена с помощью стертого serde, поэтому сериализация Box<SomeTrait> может работать.

Основная проблема - десериализация. Чтобы десериализовать полиморфный тип, вам нужно иметь маркер типа в сериализованных данных. Этот маркер должен быть сначала десериализован, а затем использован для динамического получения функции, которая будет возвращать Box<SomeTrait>,

std::any::TypeId может использоваться в качестве типа маркера, но главная проблема заключается в том, как динамически получить функцию десериализации. Я не рассматриваю возможность регистрации функции для каждого полиморфного типа, которая должна вызываться вручную во время инициализации приложения.

Я знаю два возможных способа сделать это:

  1. Языки с отражением во время выполнения, такие как C#, могут использовать его для получения метода десериализации.
  2. В C++ библиотека cereal использует магию статических объектов для регистрации десериализатора в статической карте во время инициализации библиотеки.

Но ни один из этих вариантов не доступен в Rust. Как можно добавить десериализацию полиморфных объектов в Rust?

2 ответа

Это реализовано dtolnay.

Идея довольно умная и объясняется в README:

Как это работает?

Мы используем inventory ящик для создания реестра имплитаций вашей черты, который построен на ctor ящик для подключения функций инициализации, которые вставляются в реестр. ПервыйBox<dyn Trait>десериализация выполнит работу по повторению реестра и построению карты тегов для функций десериализации. Последующие десериализации находят правильную функцию десериализации на этой карте. В erased-serde crate также задействован, чтобы сделать все это таким образом, чтобы не нарушить безопасность объекта.

Подводя итог, можно сказать, что каждая реализация признака, объявленного как [де] сериализуемый, регистрируется во время компиляции, и это разрешается во время выполнения в случае [де] сериализации объекта признака.

Все ваши библиотеки могут обеспечить процедуру регистрации, охраняемую std::sync::Once, что прописать какой-то идентификатор в общий static mut, но, очевидно, ваша программа должна вызывать их всех.

Я понятия не имею, если TypeId выдает согласованные значения при перекомпиляции с разными зависимостями.

Библиотека для этого должна быть возможной. Чтобы создать такую ​​библиотеку, мы должны создать двунаправленное отображение из TypeId на имя типа перед использованием библиотеки, а затем использовать его для сериализации / десериализации с маркером типа. Можно было бы иметь функцию для регистрации типов, которые не принадлежат вашему пакету, и предоставлять макро-аннотацию, которая автоматически делает это для типов, объявленных в вашем пакете.

Если есть способ получить доступ к идентификатору типа в макросе, это было бы хорошим способом для отображения соответствия между TypeId и именем типа во время компиляции, а не во время выполнения.

Другие вопросы по тегам