ReactiveCell<T>
Тип, в который стирается любое реактивное значение: поле, запись карты, путь
без структуры за ним, обычное значение в памяти. Коду, которому нужен «u64,
который я умею читать, писать и слушать», хватит ячейки и больше ничего: ни
структуры, на которой висит поле, ни карты с ключом, ни store с путём тащить в
свою сигнатуру не придётся.
let width = state.sidebar_width().cell();let cpu_column = state.widths().entry_cell("cpu".to_string());let by_path = store.kv().cell("dragging", 0u64)?;let loose = ReactiveCell::new_volatile(0u64);
let mut columns: HashMap<String, ReactiveCell<u64>> = HashMap::new();columns.insert("sidebar".to_string(), width);columns.insert("cpu".to_string(), cpu_column);columns.insert("dragging".to_string(), by_path);columns.insert("loose".to_string(), loose);Три из них пишут насквозь в store. ReactiveCell::new_volatile держит значение
в памяти, и процесс его не переживает. Ячейка от поля с #[amestate(volatile)]
устроена так же: у поля нет подписки на store, через которую ячейка могла бы
зафиксировать запись, — оттуда и имя.
kv.cell обходится без объявления вовсе: путь и значение по умолчанию он берёт
прямо в вызове. Тип нигде не записан, проверяет его сохранённое значение:
второй вызов по тому же пути с другим типом прочитает то, что сохранил первый,
и не сможет это декодировать.
Kv.
fn get(&self) -> Option<T>get отвечает None там, где значение взять неоткуда: запись карты, чьего
ключа нет, или ячейка, чей источник пропал. У
Field на этом месте default, поэтому
значение у него есть всегда.
Читает get кэш, который держит сама ячейка, — стоит это столько же, сколько
прочитать примитив напрямую, и дёшево настолько, что годится для цикла,
который читает каждый кадр.
let current = cell.get();
let _sub = cell.subscribe(|width| println!("width -> {width:?}"));
cell.set(200)?;cell.update(|width| width + 10)?;cell.modify(|width| *width += 10)?;Записи ложатся туда, где живёт значение
Заголовок раздела «Записи ложатся туда, где живёт значение»Ячейка пишет насквозь в то, что за ней стоит. Ячейки, чья запись доходит до кэша и там застревает, взять негде.
По дороге туда кэш не трогают: он обновится, когда store скажет, что
зафиксировал. Поэтому отклонённая запись в get не всплывёт.
Ячейка на запись карты
Заголовок раздела «Ячейка на запись карты»state.widths().insert("cpu".to_string(), &120)?;
let cpu = state.widths().entry_cell("cpu".to_string());let absent = state.widths().entry_cell("gpu".to_string());
assert_eq!(cpu.get(), Some(120));assert_eq!(absent.get(), None);
state.widths().remove("cpu")?;
assert_eq!(cpu.get(), None);assert!(cpu.set(80).is_err());Ячейка записи пуста, пока её ключа нет, и удаление ключа опустошает её снова.
set, update и modify по пустой получают отказ WriteValue::Absent с путём
записи: ячейка — проекция записи, а вернуть ключ на место может только карта.
Что ячейка держит живым
Заголовок раздела «Что ячейка держит живым»Это ровно Rc и Weak, и не по аналогии: внутри и правда лежит
Arc::downgrade на поле.
cell() и entry_cell() представляют проекцию — это Weak. Источник
такая ячейка держит слабо, и get начнёт отвечать None, как только уйдёт
последний настоящий хендл на этот источник. Как и у Weak, ячейка при этом
жива, а вот upgrade изнутри уже не удаётся — отсюда и None, и
WriteValue::SourceGone на запись.
into_cell() и into_entry_cell() делают ячейку, которая источником
владеет, — это Rc. Хендл, который вы им передали, они забирают себе и
держат сильно. Заметьте: у структуры ничего не отнимают. Вытянуть хендл из
неё вообще нельзя — state.sidebar_width() и state.widths() отдают
Arc::clone того же самого, — так что ячейка становится ещё одним владельцем,
ровно как лишний Rc::clone. За ними и тянитесь там, где
переживает всех именно ячейка: она лежит в компоненте, попадает в HashMap,
уезжает в другой поток.
let view = state.sidebar_width().cell();let owned = state.sidebar_width().into_cell();
drop(state);Обе вышли из одного поля, и живым его держит владеющая. Поэтому проекция выше
отвечает и после того, как структуру дропнули, а пустеет только тогда, когда
уйдёт и владеющая ячейка, — счётчик тот же самый, что у Rc.