Я как-то давно писал библиотеку
alkahest для сериализации со схемой.Идея её проста - разделение ответственности. Тогда как в
serde (самой популярной библиотеке для сериализации) сериализуемый и десериализуемый тип диктует схему, то начинаются проблемки, когда это два разных типа - нет никакой гарантии, что схема их совпадёт.Так же, если у вас есть данные для сериализации, но не в виде сериализуемого типа, вам обязательно нужно будет собрать их в сериализуемый тип.
Если там
HashMap, а у вас данные в итераторе - придется делать Iterator::collect только лишь за тем что бы функция сериализатора вызвала HashMap::iter.Абсурд? Абсурд. Но ёжики продолжают жрать кактус.
Что даёт явная схема?
1. Много разных типов могут сериализовываться в одну явно указываемую схему. Результат "гарантировано" будет соответствовать схеме, а значит любой десерилизуемый тип, который поддерживает эту схему, можно получить из этих данных.
Так можно сериализовывать и десериализовывать
Vec, VecDeque и итераторы, ведь все они сооветствуют схеме List Слайсы только сериализовывать.2. Сериализовывать типы, собственная структура которых является подмножеством схемы.
Например массив может писать свой размер при сериализации, если схема -
List, а в схему Array<N> не нужно.Структура может сериализовываться в определенный вариант enum-а, но все равно писать дискриминант.
Всё потому что схема диктует что писать, а сериализуемое значение только уточняет значение битов.
3. Десериализовывать типы, собственная структура которых является надмножеством схемы. Где
/dev/null считается надмножество всего.Повторяя пример с массивом, можно десериализовать схемы
Array<N> в Vec, а <Vec as Deserialize<Array<N>>>::deserialize не будет пытаться искать размер в данных.Можно пропустить поля в структуре и не десериализовывать их вовсе. Если у схемы поля фиксированный размер, то даже его читать не придется.
Или можно отложить десериализацию куска данных на потом, если всё же понадобится.
Кроме всего и ядро и сгенерированный код на Rust полностью unsafe-free, а по скорости соперничает unsafe-heavy библиотеками, такими как rkyv. Новая цель - догнать
bitcodeВсё это и многое другое уже есть в последней версии
alkahest. Зачем же я опять за него взялся?Во-первых чтобы поддержать другие языки. У вас бэк с Rust и Python, а на фронте JS? Игра на Unreal (C++), а сервер на Rust? Alkahest поможет им общаться.
Кто пользовался protobuf, Flexbuffers и Flatbuffers? Alkahest по процессу сериализации ближе всех к Flatbuffers, но круче конечно, ведь есть дженерики :)
До сих пор схема писалась либо руками (на Rust), либо генерировалась процедурным макросом из объявления структуры или энама.
Сейчас я соорудил небольшой DSL, на котором можно описывать схемы.
Но я бы ни за что не просил программистов на Rust запускать генератор кода через build-script, поэтому генерация раста происходит в проценурном макросе вот так:
#[alkahest("schemas.alk")] // Путь до файла со схемами. Если не указано, то это "<modulename>.alk".
mod schemas {} // я бы предпочел mod schemas; но на стейбле нельзя. Макрос принимает обе формы.Генерируется целый модуль, в котором будут все схемы из файла.
Старый вариант с derive-макросом для схемы остаётся для быстрого plug'n'play использования.
Для С++ будет генерация кода, которую надо будет добавить в билдскрипты.
Python будет парсить функцией, а схемы возвращаться `dict`ом.
Как правильно делать для JS я еще разберусь :)