rustsim-core 0.0.1

Core ABM engine: agents, models, stores, schedulers, stepping, data collection
Documentation
//! Deferred actions and the step context passed to agent step functions.
//!
//! During an agent step, the agent holds a mutable borrow. Direct mutation
//! of the agent store (adding/removing agents) would conflict with this
//! borrow. Instead, step functions enqueue [`DeferredAction`]s via the
//! [`StepContext`], which are applied after the current agent's step completes.

use crate::agent::Agent;
use crate::types::AgentId;

/// An action to apply after the current agent step completes.
///
/// Deferred actions avoid borrow conflicts during stepping. They are
/// collected in a buffer and applied between agent activations. Standard
/// store-only stepping applies them to the agent store. Spatial stepping
/// APIs such as `StandardModel::step_spatial` and
/// `EventQueueModel::step_event_spatial` apply the same actions to both the
/// store and the spatial index transactionally.
pub enum DeferredAction<A> {
    /// Remove an agent after the current step completes.
    RemoveAgent(AgentId),

    /// Insert an agent after the current step completes.
    InsertAgent(A),
}

/// Safe context passed to agent step functions.
///
/// Provides mutable access to space, properties, RNG, and the deferred-action
/// buffer without aliasing the agent store or the current agent's borrow.
///
/// # Type parameters
///
/// - `S` - the space type
/// - `A` - the agent type
/// - `Props` - user-defined model properties
/// - `R` - the RNG type
/// - `Sch` - the scheduler type
pub struct StepContext<'a, S, A, Props, R, Sch>
where
    A: Agent,
{
    pub(crate) space: &'a mut S,
    pub(crate) properties: &'a mut Props,
    pub(crate) rng: &'a mut R,
    pub(crate) scheduler: &'a mut Sch,
    pub(crate) deferred: &'a mut Vec<DeferredAction<A>>,

    /// PhantomData for the agent type, ensures `StepContext` is invariant
    /// in `A`.
    pub(crate) _agent: std::marker::PhantomData<A>,
}

impl<'a, S, A, Props, R, Sch> StepContext<'a, S, A, Props, R, Sch>
where
    A: Agent,
{
    /// Immutable reference to the simulation space.
    pub fn space(&self) -> &S {
        self.space
    }

    /// Mutable reference to the simulation space.
    pub fn space_mut(&mut self) -> &mut S {
        self.space
    }

    /// Immutable reference to user-defined model properties.
    pub fn properties(&self) -> &Props {
        self.properties
    }

    /// Mutable reference to user-defined model properties.
    pub fn properties_mut(&mut self) -> &mut Props {
        self.properties
    }

    /// Mutable reference to the model's RNG.
    pub fn rng(&mut self) -> &mut R {
        self.rng
    }

    /// Mutable reference to the scheduler (rarely needed by user code).
    pub fn scheduler(&mut self) -> &mut Sch {
        self.scheduler
    }

    /// Schedule an agent for removal after the current step completes.
    ///
    /// With `StandardModel::step`, the removal is applied to the store only.
    /// With `StandardModel::step_spatial`, the removal is applied to both the
    /// store and the spatial index.
    pub fn defer_remove_agent(&mut self, id: AgentId) {
        self.deferred.push(DeferredAction::RemoveAgent(id));
    }

    /// Schedule an agent for insertion after the current step completes.
    ///
    /// With `StandardModel::step`, the insertion is applied to the store only.
    /// With `StandardModel::step_spatial`, the insertion is applied to both
    /// the store and the spatial index.
    pub fn defer_insert_agent(&mut self, agent: A) {
        self.deferred.push(DeferredAction::InsertAgent(agent));
    }
}