euv_engine/cell/struct.rs
1use super::*;
2
3/// A `Sync` wrapper holding a single value of type `T`.
4///
5/// ## Storage convention
6///
7/// This type aligns with the rest of the engine's `static mut`
8/// convention used in `core::reactive::hook::impl`. The companion
9/// storage helpers `try_get` / `try_get_mut` / `get` / `get_mut`
10/// return `&'static mut T` and rely on the caller to honour the
11/// single-writer contract. The engine is compiled for
12/// `wasm32-unknown-unknown` which is single-threaded by contract,
13/// so overlapping borrows violate the caller contract rather than
14/// being trapped at runtime the way `RefCell` would.
15///
16/// `RefCell` is intentionally **not** used as the storage backend:
17/// its runtime aliasing panic is unreachable in practice under
18/// wasm's single-threaded model and adds code size and a redundant
19/// safety net that the rest of the codebase does not provide.
20///
21/// ## `?Sized` bound and trait-object storage
22///
23/// The `T: ?Sized` bound is what lets the engine alias
24/// `Rc<EngineCell<dyn Trait>>` directly (see `ComponentRc`,
25/// `SceneRc`, `TickHandlerRc` in their respective `type.rs`). The
26/// trait-object fat pointer is stored in-place inside the
27/// `UnsafeCell<T>`; `get_mut -> &'static mut dyn Trait` lets
28/// callers dispatch without an intermediate `Box`.
29///
30/// ## When to choose this vs [`MaybeEngineCell`]
31///
32/// The cell has no notion of "uninitialized" - use [`MaybeEngineCell`]
33/// if the value may legitimately be absent (the typical case for
34/// "global installed at mount, torn down at unmount" singletons).
35/// For always-present values this is a zero-overhead wrapper around
36/// `UnsafeCell<T>`.
37///
38/// ## Field access rule
39///
40/// The `inner` field is reached through the hand-written
41/// `get_inner` / `set_inner` accessors declared in `cell/impl.rs`.
42/// All read sites in this module call `get_inner().get()` and write
43/// sites use `set_inner(UnsafeCell::new(value))` rather than touching
44/// the field directly. Struct literals `Self { inner: ... }` are
45/// permitted only inside the `new` constructor and the `Default`
46/// impl, per the engine's "exceptions to no-direct-field-access"
47/// convention.
48///
49/// ## Note on `#[derive(Data)]`
50///
51/// Lombok's `Data` derive is intentionally **not** applied here:
52/// the derive requires `T: Sized`, which would defeat the `?Sized`
53/// bound above and break the engine's `Rc<EngineCell<dyn Trait>>`
54/// aliases. The accessor pair is hand-written in `cell/impl.rs`
55/// with the same Lombok-style contract.
56pub struct EngineCell<T: ?Sized> {
57 /// Raw inner storage. `pub(crate)` only because the `new`
58 /// constructor and the `Default` impl construct the struct via
59 /// a struct-literal; in all other code paths the
60 /// `get_inner` / `set_inner` accessors are the only access
61 /// points.
62 pub(crate) inner: UnsafeCell<T>,
63}
64
65/// A `Sync` wrapper that may or may not contain a value.
66///
67/// ## When to choose this vs [`EngineCell`]
68///
69/// Use this when the global state has a clear "not yet installed" or
70/// "already torn down" state. Access is gated on the inner `Option`
71/// being `Some`, so all the `try_*` accessors return `Option<...>`,
72/// matching `core::reactive::hook::try_get_current`'s wording.
73///
74/// ## Field access rule
75///
76/// Same as [`EngineCell`]: `inner` is reached only through the
77/// hand-written `get_inner` / `set_inner` accessors; the
78/// struct-literal escape hatch is reserved for the `new` / `default`
79/// constructors.
80pub struct MaybeEngineCell<T> {
81 /// Raw inner storage wrapping `Option<T>`. `pub(crate)` only
82 /// because the constructor and the `Default` impl construct the
83 /// struct via a struct-literal; in all other code paths the
84 /// accessors in `cell/impl.rs` are the only access points.
85 pub(crate) inner: UnsafeCell<Option<T>>,
86}