Я два дня сражался с тем фактом, что в Rust нельзя просто взять и написать
почему? потому, оставили невозможным что бы дизайнить взаимодействие конста и трейтов в спокойствии еще несколько лет.
Если бы без
Если что
Пробовал сделать функцию не
До некоторой глубины действительно оптимизировалось.
В итоге пришел вот к такой форме "конст-функции-на-стейбле"
Это ужасно выглядит? Да.
Это гарантирует, что в результате компиляции на месте
trait Foo {
const fn bar(arg: Arg) -> Ret;
}почему? потому, оставили невозможным что бы дизайнить взаимодействие конста и трейтов в спокойствии еще несколько лет.
Если бы без
Arg - я бы просто сделал const BAR: Ret.Если что
const BAR<const ARG: Arg>; Тоже нельзя, но хотя бы в найтли можно.Пробовал сделать функцию не
const, но писать её, как если бы она была const. Надеялся, что соптимизируется.До некоторой глубины действительно оптимизировалось.
В итоге пришел вот к такой форме "конст-функции-на-стейбле"
trait Foo {
type Bar<const ARG: Arg>: RetType;
}
trait RetType {
const VALUE: Ret;
}
impl Foo for FooImpl {
type Bar<const ARG: Arg> = FooBar<ARG>;
}
struct FooBar<const ARG: Arg>;
impl RetType for FooBar<const ARG: Arg> {
const VALUE: Ret = {
// faked const fn body.
};
}Это ужасно выглядит? Да.
Это гарантирует, что в результате компиляции на месте
FooImpl::Bar::<ARG>::VALUE будет константа, а не вызов функции? Тоже да.❤6👍4🔥1
Я снова решил поупражняться в сериализаторском деле.
Я как-то давно писал библиотеку
Идея её проста - разделение ответственности. Тогда как в
Так же, если у вас есть данные для сериализации, но не в виде сериализуемого типа, вам обязательно нужно будет собрать их в сериализуемый тип.
Если там
Абсурд? Абсурд. Но ёжики продолжают жрать кактус.
Что даёт явная схема?
1. Много разных типов могут сериализовываться в одну явно указываемую схему. Результат "гарантировано" будет соответствовать схеме, а значит любой десерилизуемый тип, который поддерживает эту схему, можно получить из этих данных.
Так можно сериализовывать и десериализовывать
2. Сериализовывать типы, собственная структура которых является подмножеством схемы.
Например массив может писать свой размер при сериализации, если схема -
Структура может сериализовываться в определенный вариант enum-а, но все равно писать дискриминант.
Всё потому что схема диктует что писать, а сериализуемое значение только уточняет значение битов.
3. Десериализовывать типы, собственная структура которых является надмножеством схемы. Где
Повторяя пример с массивом, можно десериализовать схемы
Можно пропустить поля в структуре и не десериализовывать их вовсе. Если у схемы поля фиксированный размер, то даже его читать не придется.
Или можно отложить десериализацию куска данных на потом, если всё же понадобится.
Кроме всего и ядро и сгенерированный код на Rust полностью unsafe-free, а по скорости соперничает unsafe-heavy библиотеками, такими как rkyv. Новая цель - догнать
Всё это и многое другое уже есть в последней версии
Во-первых чтобы поддержать другие языки. У вас бэк с Rust и Python, а на фронте JS? Игра на Unreal (C++), а сервер на Rust? Alkahest поможет им общаться.
Кто пользовался protobuf, Flexbuffers и Flatbuffers? Alkahest по процессу сериализации ближе всех к Flatbuffers, но круче конечно, ведь есть дженерики :)
До сих пор схема писалась либо руками (на Rust), либо генерировалась процедурным макросом из объявления структуры или энама.
Сейчас я соорудил небольшой DSL, на котором можно описывать схемы.
Но я бы ни за что не просил программистов на Rust запускать генератор кода через build-script, поэтому генерация раста происходит в проценурном макросе вот так:
Генерируется целый модуль, в котором будут все схемы из файла.
Старый вариант с derive-макросом для схемы остаётся для быстрого plug'n'play использования.
Для С++ будет генерация кода, которую надо будет добавить в билдскрипты.
Python будет парсить функцией, а схемы возвращаться `dict`ом.
Как правильно делать для JS я еще разберусь :)
Я как-то давно писал библиотеку
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 я еще разберусь :)
🔥6❤1
Во-вторых чтобы реализовать верифицируемую эволюцию схемы.
Схемы очень часто изменяются с выходом новых версий программы. Общающиеся программы обновляются не синхронно и надо как-то им уметь продолжать общение несмотря на то что у кого-то схема новее, у кого-то древнее. Так же сохраненные данные могут быть старой версии.
Для этого схемы должны обладать совместимостью. Совместимость бывает прямой и обратной, давайте рассмотрим оба случая.
Обратная совместимость означает, что данные сериализованные старой версией схемы могут быть десериализованны новой версией.
Прямая совместимость означает, что данные сериализованные с новой версией схемы могут быть десериализованны старой версией.
Как такое возможно? Десериализатор должен узнать какая версия схемы у данных, либо снаружи, либо из данных. А так же должна быть стратегия того, как конвертировать данные, например какое значение новым полям указать.
Это уже сразу несколько вариантов. Alkahest точно будет поддерживать вариант с указанием версии снаружи и указанием версии прямо в данных.
Но скорее всего не будет варианта с указанием в данных версии для субструктур, всё дерево схемы будет с одной версией.
С прямой совместимостью есть еще вариант без указания версии. Если новая версия строго шире старой, то десериализатор со старой схемой просто игнорирует хвост.
Тут помогает сериализация с указателем, потому что размер поля указателя фиксированный, так что схема может обладать прямой совместимостью и не влиять на схему, полем которой она является, если используется указатель.
То есть если было:
То старая формула
Что тут будет делать Alkahest? Проверять совместимость! Автор схемы указывает вид совместимости, а функция верификации убедится, что новая схема обладает указанной совместимостью со старой схемой.
Ультой будет проверка диффа кода на наличие несовместимых изменений схемы. И GitHub Action для запуска этой проверки в PR.
Схемы очень часто изменяются с выходом новых версий программы. Общающиеся программы обновляются не синхронно и надо как-то им уметь продолжать общение несмотря на то что у кого-то схема новее, у кого-то древнее. Так же сохраненные данные могут быть старой версии.
Для этого схемы должны обладать совместимостью. Совместимость бывает прямой и обратной, давайте рассмотрим оба случая.
Обратная совместимость означает, что данные сериализованные старой версией схемы могут быть десериализованны новой версией.
Прямая совместимость означает, что данные сериализованные с новой версией схемы могут быть десериализованны старой версией.
Как такое возможно? Десериализатор должен узнать какая версия схемы у данных, либо снаружи, либо из данных. А так же должна быть стратегия того, как конвертировать данные, например какое значение новым полям указать.
Это уже сразу несколько вариантов. Alkahest точно будет поддерживать вариант с указанием версии снаружи и указанием версии прямо в данных.
Но скорее всего не будет варианта с указанием в данных версии для субструктур, всё дерево схемы будет с одной версией.
С прямой совместимостью есть еще вариант без указания версии. Если новая версия строго шире старой, то десериализатор со старой схемой просто игнорирует хвост.
Тут помогает сериализация с указателем, потому что размер поля указателя фиксированный, так что схема может обладать прямой совместимостью и не влиять на схему, полем которой она является, если используется указатель.
То есть если было:
formula A = { ... }
formula B = { ... }
formula Foo = {
a: A,
b: B,
};
formula Bar = {
a: &A,
b: &B,
};То старая формула
Foo не будет совместимой с новой версией, если A расширилось, а Bar будет.Что тут будет делать Alkahest? Проверять совместимость! Автор схемы указывает вид совместимости, а функция верификации убедится, что новая схема обладает указанной совместимостью со старой схемой.
Ультой будет проверка диффа кода на наличие несовместимых изменений схемы. И GitHub Action для запуска этой проверки в PR.
👍1🔥1
Интересным образом оказывается, что не так уж сложно сериализовывать
Без специализации и special-casing-а.
Просто дискриминанты с uninhabited схемами не считаются.
А значит у
Option<!> в 0 байт.Без специализации и special-casing-а.
Просто дискриминанты с uninhabited схемами не считаются.
А значит у
Option<!> только 1 дискриминант None. Значит надо 0 байт на него, и на его тело тоже 0 байт.🤣2💯1👀1
Еще одно очевидное свойство
Его можно сериализовать в любую схему!
В самом деле, если мы сериализуем значение
На практике
А вот десериализовывать
Потому что эта схема не имеет представления в данных.
На практике
! типа и сериализации.Его можно сериализовать в любую схему!
В самом деле, если мы сериализуем значение
!, то как известно этот код недостижим. А значит не важно, какая там схема.На практике
! будет у варианта `enum`а, который невозможен. Значит сериализовываться будет всегда какой-то другой вариант или вообще не будет.А вот десериализовывать
! можно только из схемы !.Потому что эта схема не имеет представления в данных.
На практике
! будет у варианта схемы enum`а, которому дискриминант не назначен вовсе, а значит какие бы ни были данные, а функция десериализации ! не будет вызвана.Загадка про Rust
Что происходит с атрибутом
Что происходит с атрибутом
#[cold] над const функцией, когда её вызов вычисляется еще до LLVM?#[cold]
const fn mark_cold() {}
if condition {
mark_cold();
// Это холодный бранч или нет?
}
🤔6
О как.
Оказывается буквально 22 января в стейбл приехали новые методы для слайсов.
Оказывается буквально 22 января в стейбл приехали новые методы для слайсов.
<[MaybeUninit<T>]>::assume_init_* семейство, меньше трансмутить. Трансмутить можно, но всегда опасное.<[T]>::as_array и компания - ура, минус причина писать unsafe.👍8❤1🔥1👀1
Как заставить растовика задуматься над элементарной вещью?
Задайте ему вопрос: Какой тип у `0x12345_f32`?
Задайте ему вопрос: Какой тип у `0x12345_f32`?
Anonymous Quiz
62%
f32
5%
f64
5%
usize
5%
u32
12%
i32
5%
str
2%
!
2%
()
2%
Свой вариант в комментарии
🔥4
Написал свой парсер литералов в лексере.
Так он занял больше кода, чем весь остальной лексер.
Анэскейпинг хотя бы пошарился между символом и строкой.
А вот с числами сильно больше ожидаемого краевых случаев.
Так он занял больше кода, чем весь остальной лексер.
Анэскейпинг хотя бы пошарился между символом и строкой.
А вот с числами сильно больше ожидаемого краевых случаев.
🔥4
Спрашивал у ИИ на счёт квантовой теории. Оно мне между делом такое выдало:
Your body mass is mostly vibrating strong field energy.
Which is deeply unfair because you still have to exercise.
Your body mass is mostly vibrating strong field energy.
Which is deeply unfair because you still have to exercise.
😁13😐2🥴1
Media is too big
VIEW IN TELEGRAM
Когда пришёл на босса, но у тебя нет времени уворачиваться от атак.
😎3