Skip to main content

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}