Что здесь значит serde
Библиотека хранит всё, что донесёт serde, — а значит, тип для хранения написан сразу на двух языках. Эта страница про то, где между ними проходит граница, а проходит она в одном месте: сериализуется тип или нет.
Структура, чьи поля — пути (с #[amethystate] или встроенная через
nested) не сериализуется никогда. Не «её никто не сериализует», а Serialize
для неё не пишется: это не значение, а описание того, где лежат другие значения.
Каждое её поле уходит в store само по себе, своим путём и в свой черёд.
Поэтому #[serde(..)], написанный на ней или на её поле, — ошибка компиляции.
И притом не наша, а rustc:
error: cannot find attribute `serde` in this scope = note: `serde` is an attribute that can be used by the derive macros `Deserialize` and `Serialize`, you might be missing a `derive` attributeЭтим всё и исчерпано. Макрос не читает serde, не разбирает его по пунктам и ничего про него не объясняет: он переносит на генерируемое поле всякий атрибут, которого не понимает, — и судит атрибут тот, кто его понимает. Нет вывода — нет и атрибута.
Лист — всё, что лежит одним значением по одному пути, — наоборот. Store отдаёт его кодеку целиком, забирает байты и внутрь не смотрит, и любой serde-атрибут значит там ровно то же, что везде.
Чем это сказать здесь
Заголовок раздела «Чем это сказать здесь»У пяти вещей, ради которых обычно берут serde, здесь есть своё место. Написание разведено потому, что операции разные:
| у serde | здесь |
|---|---|
rename = ".." |
#[amestate(path = "..")] — и точка в нём уровень, тогда как у serde это одно имя, в котором случилась точка |
rename_all = ".." |
rename_all в #[amethystate(..)] сверху |
default на поле |
#[amestate(default = ..)], берущий выражение, а не путь к функции |
flatten |
#[amestate(flatten)] на nested-поле — и он двигает сегменты на диске, тогда как у serde вливает карту в карту |
with, serialize_with, deserialize_with |
#[amestate(with = ..)] и его половины, теми же функциями |
Остальному словарю serde здесь делать нечего. deny_unknown_fields, tag,
untagged, transparent, from, remote, skip, alias, getter,
borrow — все они описывают одно закодированное значение, а его нет: поля
лежат отдельными путями и отсутствуют каждое само по себе. За путь, который не
читается, отвечает on_unreadable; поле, которое вообще не попадает в store, —
это #[amestate(volatile)]; данные, записанные под старым именем, переносит шаг
#[migrate] с #[rename(old => new)] — один раз при открытии и на том конец,
тогда как алиас пришлось бы держать вечно.
Всё это описано там же, где живёт словарь: Поля.
Внутри листа
Заголовок раздела «Внутри листа»Ничего из написанного выше здесь не действует. Store кладёт значение целиком по одному пути и читает его обратно, а что ему сказали по дороге — дело ваше и serde:
#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]#[serde(rename_all = "kebab-case", deny_unknown_fields)]pub struct Endpoint { pub host_name: String,
#[serde(default)] pub port: u16,}
#[amethystate(prefix = "svc")]pub struct Service { #[amestate(default = Endpoint { host_name: "localhost".to_string(), port: 443 })] pub upstream: Endpoint,}Endpoint ложится целиком в svc.upstream — в kebab-case и строгим к
неизвестным полям, — и у store на этот счёт мнения нет.
Две вещи всё же чего-то стоят, и стоит знать, каких:
Переименование внутри листа — миграция, которой здесь никто не заметит. Байты переехали, имя в Rust нет. В store, записанном старой сборкой, внутри значения лежит старое имя поля, и новая сборка его не найдёт.
deny_unknown_fields превращает каждое поле, добавленное позже, в жёсткий
отказ чтения для старой сборки. proto3 выбрасывал неизвестные поля в первом
релизе и передумал в 3.5.0 ровно поэтому. Значение ваше, и решение ваше.
И две привычки, которые стоит держать — по той же причине, по какой их держит любой формат:
#[serde(default)] на каждом поле, добавленном после первого релиза. Serde
и так пропускает поле, которого не знает, поэтому значение, записанное новой
сборкой, читается старой даром. В обратную сторону даром не выходит: полю,
которому нечем заполниться, нечего и подставить — оно валит всё значение целиком,
и лист, записанный до появления поля, перестаёт читаться.
Именованные поля, а не кортеж. Кортежная структура сериализуется по позициям, так что поле, вставленное в середину, молча сдвигает все следующие.
Что формат вообще донесёт — другой список
Заголовок раздела «Что формат вообще донесёт — другой список»Ничего из этого не про то, что движок способен удержать. Это измерено и лежит в
Выборе движка: ron не понесёт enum, json и sqlite
не понесут NaN, у toml нет места дальше i64. Там отказывают значению, а
эта страница — про тип, который говорит то, чего store не может записать.
Тип может пройти один список и не пройти другой.