Когда значение не читается
Три момента, у каждого свой ответ: структура открывается над байтами, которые не декодируются; под живым полем удалили ключ; пришло изменение, и оно не декодируется. Значение, которое декодируется прекрасно и всё равно бессмысленно, — другое дело, и отвечают на него Правила.
Открытие
Заголовок раздела «Открытие»Если по объявленному пути лежит то, что не декодируется в тип поля, структура
не построится и назовёт этот путь. Это Refuse, он по умолчанию: new_with
паникует, называя путь и то, что сказал кодек, а try_new_with отдаёт то же
самое как Err. UseDefault
— для приложения, которое обязано запуститься при любом раскладе: поле берёт
значение из default, испорченное значение остаётся на диске, чтобы его
кто-нибудь починил, а try_get отвечает
Err — с самого построения и до первого изменения, которое декодируется.
#[amethystate(prefix = "mixed", on_unreadable = UseDefault)]pub struct Mixed { #[amestate(default = 8080u16)] pub port: u16,
#[amestate(default = "".to_string(), on_unreadable = Refuse)] pub licence: String,}Поле вправе ужесточить то, что сказала структура. Выше настройки откроются
со сломанным port, а нечитаемый licence остановит всё. Обратное — Refuse
на структуре и UseDefault на поле — не соберётся, и компилятор назовёт это
поле. Вложенная структура наследует ответ той, что её держит, ужесточает его
так же и сверяется с ней на компиляции.
Где не сказал никто, говорит store. Сказанное полем бьёт сказанное структурой, а сказанное структурой — то, с чем store открыли. Так что приложение один раз говорит, чего хочет от всего, у чего мнения не было, — а всё объявленное выше остаётся нетронутым: Как открывают store.
Неудавшееся построение говорит, как именно оно не удалось. try_new_with
отдаёт OpenStruct — те способы упасть, что есть у конструктора, и никаких
других, — так что один случай отличают от другого по варианту, а не по тексту:
match Strict::try_new_with(&store) { Ok(_) => {} Err(OpenStruct::WillNotRead { at, why }) => eprintln!("{at} is unreadable: {why}"), Err(OpenStruct::Taken(taken)) => { eprintln!("{} already holds {}", taken.held_by, taken.at) } Err(other) => return Err(other.into()),}При UseDefault ничего не падает, и то же место с той же причиной приходят
через поле — как Disagreement: это не провал самого вопроса, а то, о чём поле
и store не договорились.
let held = match state.port().try_get() { Ok(port) => port, Err(no) => { match no.reason { Reason::WillNotRead(said) => eprintln!("{} will not decode: {said}", no.at), Reason::Occupied(said) => eprintln!("{} was already taken: {said}", no.at), _ => eprintln!("{} is not what the store has", no.at), }
state.port().get() }};Байты, которые не декодировались, приходят как WillNotRead — с фразой самого
кодека, — а поле, так и не записавшее свой default, потому что store уже
что-то там держал, как Occupied. Поле закрытого store отвечает
Reason::Closed: значение в нём последнее, что оно слышало. А
Reason::NotWrittenBack значит, что правило
поправило сохранённое значение, а store поправку не взял.
Reason помечен #[non_exhaustive] — это список того, в чём поле и store
бывают не согласны, и он растёт, — поэтому match по нему держит ветку _.
Наборы её не держат, и Ошибки объясняют
почему.
Ключ удалили под живым полем
Заголовок раздела «Ключ удалили под живым полем»Поле продолжает показывать последнее, что держало, — то самое, что было на
экране мгновение назад. Против него default всего лишь догадка, сделанная на
компиляции. UseDefault просит именно догадку:
#[amethystate(prefix = "mixed_delete")]pub struct MixedDelete { #[amestate(default = 800u32)] pub width: u32,
#[amestate(default = 600u32, on_delete = UseDefault)] pub height: u32,}Пришло изменение, и оно не декодируется
Заголовок раздела «Пришло изменение, и оно не декодируется»Поле держит последнее значение, с которым store был согласен, подписчиков
никто не зовёт. try_get про это скажет и замолчит, как только очередное
изменение декодируется. Объявлять тут нечего.