Rate this Page
Actor Trait">

The Actor Trait#

The Actor trait defines the core behavior of all actors in the hyperactor runtime.

Every actor type must implement this trait to participate in the system. It defines how an actor is constructed, initialized, and supervised.

#[async_trait]
pub trait Actor: Sized + Send + Debug + 'static {
    type Params: Send + 'static;

    async fn new(params: Self::Params) -> Result<Self, anyhow::Error>;

    async fn init(&mut self, _this: &Instance<Self>) -> Result<(), anyhow::Error> {
        Ok(())
    }

    async fn spawn(
        cap: &impl cap::CanSpawn,
        params: Self::Params,
    ) -> anyhow::Result<ActorHandle<Self>> {
        cap.spawn(params).await
    }

    async fn spawn_detached(params: Self::Params) -> Result<ActorHandle<Self>, anyhow::Error> {
        Proc::local().spawn("anon", params).await
    }

    fn spawn_server_task<F>(future: F) -> JoinHandle<F::Output>
    where
        F: Future + Send + 'static,
        F::Output: Send + 'static,
    {
        tokio::spawn(future)
    }

    async fn handle_supervision_event(
        &mut self,
        _this: &Instance<Self>,
        _event: &ActorSupervisionEvent,
    ) -> Result<bool, anyhow::Error> {
        Ok(false)
    }

    async fn handle_undeliverable_message(
        &mut self,
        this: &Instance<Self>,
        Undeliverable(envelope): Undeliverable<MessageEnvelope>,
    ) -> Result<(), anyhow::Error> {
        assert_eq!(envelope.sender(), this.self_id());

        anyhow::bail!(UndeliverableMessageError::delivery_failure(&envelope));
    }
}

Construction: Params and new#

Each actor must define a Params type:

type Params: Send + 'static;

This associated type defines the data required to instantiate the actor.

The actor is constructed by the runtime using:

async fn new(params: Self::Params) -> Result<Self, anyhow::Error>;

This method returns the actor’s internal state. At this point, the actor has not yet been connected to the runtime; it has no mailbox and cannot yet send or receive messages. new is typically used to construct the actor’s fields from its input parameters.

Initialization: init#

async fn init(&mut self, this: &Instance<Self>) -> Result<(), anyhow::Error>

The init method is called after the actor has been constructed with new and registered with the runtime. It is passed a reference to the actor’s Instance, allowing access to runtime services such as:

  • The actor’s ID and status

  • The mailbox and port system

  • Capabilities for spawning or sending messages

The default implementation does nothing and returns Ok(()).

If init returns an error, the actor is considered failed and will not proceed to handle any messages.

Use init to perform startup logic that depends on the actor being fully integrated into the system.

Spawning: spawn#

The spawn method provides a default implementation for creating a new actor from an existing one:

async fn spawn(
    cap: &impl cap::CanSpawn,
    params: Self::Params,
) -> anyhow::Result<ActorHandle<Self>> {
    cap.spawn(params).await
}

In practice, CanSpawn is only implemented for Instance<A>, which represents a running actor. As a result, Actor::spawn(...) always constructs a child actor: the new actor receives a child ID and is linked to its parent through the runtime.

Detached Spawning: spawn_detached#

async fn spawn_detached(params: Self::Params) -> Result<ActorHandle<Self>, anyhow::Error> {
    Proc::local().spawn("anon", params).await
}

This method creates a root actor on a fresh, isolated proc.

  • The proc is local-only and cannot forward messages externally.

  • The actor receives a unique root ActorId with no parent.

  • No supervision or linkage is established.

  • The actor is named "anon".

Background Tasks: spawn_server_task#

fn spawn_server_task<F>(future: F) -> JoinHandle<F::Output>
where
    F: Future + Send + 'static,
    F::Output: Send + 'static,
{
    tokio::spawn(future)
}

This method provides a hook point for customizing how the runtime spawns background tasks.

By default, it simply calls tokio::spawn(...) to run the given future on the Tokio executor.

Supervision Events: handle_supervision_event#

async fn handle_supervision_event(
    &mut self,
    _this: &Instance<Self>,
    _event: &ActorSupervisionEvent,
) -> Result<bool, anyhow::Error> {
    Ok(false)
}

This method is invoked when the runtime delivers an ActorSupervisionEvent to the actor — for example, when a child crashes or exits.

By default, it returns Ok(false), which indicates that the event was not handled by the actor. This allows the runtime to fall back on default behavior (e.g., escalation).

Actors may override this to implement custom supervision logic.

Undeliverables: handle_undeliverable_message#

async fn handle_undeliverable_message(
    &mut self,
    this: &Instance<Self>,
    Undeliverable(envelope): Undeliverable<MessageEnvelope>,
) -> Result<(), anyhow::Error> {
    assert_eq!(envelope.sender(), this.self_id());

    anyhow::bail!(UndeliverableMessageError::delivery_failure(&envelope));
}

This method is called when a message sent by this actor fails to be delivered.

  • It asserts that the message was indeed sent by this actor.

  • Then it returns an error: Err(UndeliverableMessageError::DeliveryFailure(...))

This signals that the actor considers this delivery failure to be a fatal error. You may override this method to suppress the failure or to implement custom fallback behavior.