pub struct FrameStartView<'a> { /* private fields */ }Expand description
A borrowing view over a pinned frame-start World: reads are
served by reference from the shared &World, writes land in a private
per-view overlay that the shared world never sees.
This is docs/effects-spec.md §12.2’s “borrow, don’t copy” primitive
(issue #937). It exists for batch-mode stepping, where N flows must each
advance against the same frame-start state while their writes stay
private until a later, ordered Apply pass. The obvious way to get that
is to hand every flow its own frame_start.clone(); the cost of that
clone is O(world size) per flow, per turn — every global Value,
every visit/turn-count entry — which is nearly free for a scalar toy
world and emphatically not free for a real game’s.
FrameStartView pays O(1) to construct and O(cells this flow actually wrote) thereafter. It is observationally identical to
stepping against a private clone:
- a read returns the overlay’s value if this view has written that
cell, else the frame-start value — i.e.
frame_start ⊕ own writes, exactly what a clone would hold; - a write only ever mutates the overlay, so the borrowed
&World(and therefore every peer view over it) is untouched; - an increment (
increment_visit,increment_turn_index) is copy-on-write from that same read-through value, so a flow’s first increment starts from the frame-start count rather than 0.
Because it borrows shared-immutably, many FrameStartViews over one
&World can run concurrently — the property bevy-brink’s parallel
batch driver needs, and the reason this type takes &World rather than
the &mut World ContextView requires. The semantics are those of
Mode::Sandbox (every unit treated as flow-private, the shared world
read-only), reachable here without a &mut borrow and without a
FlowLocal chain — this view has no frozen base and consults no
ResolvedPolicy, because every cell is unconditionally overlaid.
The overlay is intentionally not readable back out: the authoritative
record of what a flow wrote is the
WriteObserver callback stream, which a caller
gets by wrapping this view in an
ObservedContext. Keeping one changeset
record instead of two is what makes the buffered-write Apply pass
trivially consistent with what the flow actually observed.
Implementations§
Trait Implementations§
Source§impl ContextAccess for FrameStartView<'_>
impl ContextAccess for FrameStartView<'_>
Source§fn take_global(&mut self, idx: u32) -> Value
fn take_global(&mut self, idx: u32) -> Value
A real core::mem::replace move once the slot is in the overlay —
which is what keeps docs/value-model-spec.md §5’s take → make_mut
→ write-back discipline O(1)-amortized here, exactly as it is
against a private clone. The first take of a given slot must
still clone out of the borrowed frame-start world (it is shared; this
view may not move out of it), but that is one Arc bump for one
cell — the same bump a whole-world clone would have paid for that
cell up front, and every subsequent take of the slot is a move.
fn global(&self, idx: u32) -> &Value
fn set_global(&mut self, idx: u32, value: Value)
fn visit_count(&self, id: DefinitionId) -> u32
fn increment_visit(&mut self, id: DefinitionId)
Source§fn set_visit_count(&mut self, id: DefinitionId, count: u32)
fn set_visit_count(&mut self, id: DefinitionId, count: u32)
crate::load_state to reconcile a durable save, whose entries carry
absolute counts rather than deltas.fn turn_count(&self, id: DefinitionId) -> Option<u32>
fn set_turn_count(&mut self, id: DefinitionId, turn: u32)
fn turn_index(&self) -> u32
fn increment_turn_index(&mut self)
Source§fn set_turn_index(&mut self, index: u32)
fn set_turn_index(&mut self, index: u32)
crate::load_state to restore a saved turn index.