rustsim_core/step_context.rs
1//! Deferred actions and the step context passed to agent step functions.
2//!
3//! During an agent step, the agent holds a mutable borrow. Direct mutation
4//! of the agent store (adding/removing agents) would conflict with this
5//! borrow. Instead, step functions enqueue [`DeferredAction`]s via the
6//! [`StepContext`], which are applied after the current agent's step completes.
7
8use crate::agent::Agent;
9use crate::types::AgentId;
10
11/// An action to apply after the current agent step completes.
12///
13/// Deferred actions avoid borrow conflicts during stepping. They are
14/// collected in a buffer and applied between agent activations. Standard
15/// store-only stepping applies them to the agent store. Spatial stepping
16/// APIs such as `StandardModel::step_spatial` and
17/// `EventQueueModel::step_event_spatial` apply the same actions to both the
18/// store and the spatial index transactionally.
19pub enum DeferredAction<A> {
20 /// Remove an agent after the current step completes.
21 RemoveAgent(AgentId),
22
23 /// Insert an agent after the current step completes.
24 InsertAgent(A),
25}
26
27/// Safe context passed to agent step functions.
28///
29/// Provides mutable access to space, properties, RNG, and the deferred-action
30/// buffer without aliasing the agent store or the current agent's borrow.
31///
32/// # Type parameters
33///
34/// - `S` - the space type
35/// - `A` - the agent type
36/// - `Props` - user-defined model properties
37/// - `R` - the RNG type
38/// - `Sch` - the scheduler type
39pub struct StepContext<'a, S, A, Props, R, Sch>
40where
41 A: Agent,
42{
43 pub(crate) space: &'a mut S,
44 pub(crate) properties: &'a mut Props,
45 pub(crate) rng: &'a mut R,
46 pub(crate) scheduler: &'a mut Sch,
47 pub(crate) deferred: &'a mut Vec<DeferredAction<A>>,
48
49 /// PhantomData for the agent type, ensures `StepContext` is invariant
50 /// in `A`.
51 pub(crate) _agent: std::marker::PhantomData<A>,
52}
53
54impl<'a, S, A, Props, R, Sch> StepContext<'a, S, A, Props, R, Sch>
55where
56 A: Agent,
57{
58 /// Immutable reference to the simulation space.
59 pub fn space(&self) -> &S {
60 self.space
61 }
62
63 /// Mutable reference to the simulation space.
64 pub fn space_mut(&mut self) -> &mut S {
65 self.space
66 }
67
68 /// Immutable reference to user-defined model properties.
69 pub fn properties(&self) -> &Props {
70 self.properties
71 }
72
73 /// Mutable reference to user-defined model properties.
74 pub fn properties_mut(&mut self) -> &mut Props {
75 self.properties
76 }
77
78 /// Mutable reference to the model's RNG.
79 pub fn rng(&mut self) -> &mut R {
80 self.rng
81 }
82
83 /// Mutable reference to the scheduler (rarely needed by user code).
84 pub fn scheduler(&mut self) -> &mut Sch {
85 self.scheduler
86 }
87
88 /// Schedule an agent for removal after the current step completes.
89 ///
90 /// With `StandardModel::step`, the removal is applied to the store only.
91 /// With `StandardModel::step_spatial`, the removal is applied to both the
92 /// store and the spatial index.
93 pub fn defer_remove_agent(&mut self, id: AgentId) {
94 self.deferred.push(DeferredAction::RemoveAgent(id));
95 }
96
97 /// Schedule an agent for insertion after the current step completes.
98 ///
99 /// With `StandardModel::step`, the insertion is applied to the store only.
100 /// With `StandardModel::step_spatial`, the insertion is applied to both
101 /// the store and the spatial index.
102 pub fn defer_insert_agent(&mut self, agent: A) {
103 self.deferred.push(DeferredAction::InsertAgent(agent));
104 }
105}