Reference

API Overview

All the key types, traits, and macros in one place. For complete API documentation, see docs.rs/acton-reactive.

Core Types

ActonApp

The entry point. Its only job is to boot the system and hand you an ActorRuntime.

let mut runtime = ActonApp::launch_async().await;
MethodDescription
launch_async().awaitStart a new actor system (use this in async contexts)
launch()Start from a sync context. Panics if called inside a Tokio runtime.

ActorRuntime

What launch_async() returns. This is where actor creation, the broker, shutdown, and IPC live.

MethodDescription
new_actor::<T>()Create an actor builder (default name)
new_actor_with_name::<T>(name)Create a named actor builder
new_actor_with_config::<T>(config)Create a builder from an ActorConfig
spawn_actor::<T>(config, setup)Create, configure, and start in one call
spawn_actor_with_setup_fn::<T>(config, setup_fn)As above, with an async setup closure
broker()Access the message broker
actor_count()Number of top-level actors
shutdown_all().awaitGraceful shutdown of the whole system
ipc_registry()Access IPC type registry
ipc_expose(name, handle)Expose actor for IPC. Returns Result<(), IpcNameInUse>; it no longer replaces an existing registration
ipc_hide(name)Remove IPC exposure
ipc_lookup(name)Find an IPC-exposed actor
start_ipc_listener().awaitStart IPC listener (default config)
start_ipc_listener_with_config(cfg).awaitStart IPC listener with custom config

IPC methods require the ipc feature.

Actor Builder

ManagedActor<Idle, T> — configures an actor before spawning.

Handler-registration methods take &mut self and return &mut Self, while start() consumes the builder. So configure first, then start — you can't chain straight through into .start():

let mut builder = runtime.new_actor::<Counter>();
builder
    .mutate_on::<Increment>(handler)
    .act_on::<GetCount>(handler);

let handle = builder.start().await;
MethodDescription
mutate_on::<M>(handler)Register async state-changing handler
mutate_on_sync::<M>(handler)Register sync state-changing handler (no future allocation)
act_on::<M>(handler)Register async read-only handler
act_on_sync::<M>(handler)Register sync read-only handler (no future allocation)
try_mutate_on::<M, T, E>(handler)Async state-changing handler returning Result<T, E>
try_act_on::<M, T, E>(handler)Async read-only handler returning Result<T, E>
on_error::<M, E>(handler)Handle error E returned by a try_* handler for message M
before_start(hook)Lifecycle hook before the message loop starts
after_start(hook)Lifecycle hook after the message loop starts
before_stop(hook)Lifecycle hook before shutdown begins
after_stop(hook)Lifecycle hook after the message loop ends
create_child(name)Build a child actor under this one (Idle only — not available inside a handler)
expose_for_ipc()Expose actor for IPC using its ERN root name
start().awaitSpawn the actor, consuming the builder; returns its ActorHandle
handle()Get the handle before starting

ActorHandle

Reference to a running actor.

handle.send(Message).await;
handle.stop().await.ok();
MethodDescription
send(msg).awaitFire-and-forget message
ask(request).awaitSend a request and wait for the reply
ask_with_timeout(request, dur).awaitask with an explicit deadline
broadcast(msg).awaitPublish to the broker for all subscribers
stop().awaitStop the actor
subscribe::<M>().awaitSubscribe to broadcast messages
unsubscribe::<M>()Unsubscribe from broadcast messages (fire-and-forget)
unsubscribe_async::<M>().awaitUnsubscribe, awaiting delivery of the request to the broker
reply_address()Get this actor's address, for use as a return address
create_envelope(recipient)Create an envelope from this actor to recipient
supervise_with::<S>(&runtime, config, blueprint).awaitRegister a child with a blueprint; returns SupervisedChild. The framework restarts it
supervise(child).awaitAdopt an already-built actor for cascading shutdown. No blueprint, so never restarted
unsupervise(&ern).awaitRetire the record and stop the child, dropping its IPC names
release(&ern).awaitRetire the record and hand the child back still running
children()Children supervised through this actor's handles — every clone shares one map; handles go stale across a restart
find_child(&ern)Look up a direct child by ERN, with the same caveat
id()Get actor's identifier (Ern)
name()Get actor's root name

ask and Request

ask sends a request and waits for the reply. A message becomes askable by implementing Request, which names the reply through an associated type:

impl Request for GetCount {
    type Response = Count;
}

let count = handle.ask(GetCount).await?;

Because inboxes are FIFO, a completed ask also proves every message sent to that actor beforehand has been processed. Handlers are unchanged: they answer through the reply envelope, and an actor cannot tell an ask from a send.

ask always finishes, backed by a 30-second DEFAULT_ASK_TIMEOUT. Outcomes come back as AskError, which is #[non_exhaustive]:

VariantMeaning
NoReplyDelivered, but no answer is coming
TimedOut { after }The reply address is still live; the deadline expired
UndeliverableThe inbox was already closed
CancelledDelivery was abandoned during shutdown
UnexpectedReplyThe handler answered with a type the request does not declare
PeerRejected { code, detail }(IPC) The peer refused before dispatch; a retry is safe
TransportFailed { detail }(IPC) The connection failed; whether it was processed is unknown

Do not ask from inside a mutate_on handler. Mutable handlers are awaited inline on the message loop, so waiting for a reply stops the actor from processing the message that would produce it.

SupervisedChild

A reference to a supervised child that survives its restarts. ActorHandle names one incarnation and goes stale; SupervisedChild reads a status channel its supervisor publishes to.

MethodDescription
current()Handle for the incarnation running right now
wait_running().awaitBlock until an incarnation is up; errors once escalated
wait_generation(gen).awaitBlock until a specific incarnation is up
status()The published SupervisionStatus: state, generation, last reason

SupervisionState is Starting, Running, RestartPending, Restarting, Down (will not come back), or Escalated (allowance exhausted).

FlushBroadcasts

A barrier for broadcasts. broadcast completes when the broker has the message, not when subscribers do, and a broadcast carries no reply address for a subscriber to answer.

broker.ask(FlushBroadcasts).await?;   // answers BroadcastsFlushed

The reply cannot arrive until every earlier broadcast is sitting in every subscriber's inbox. That is delivery, not completion: to know a particular subscriber has handled it, ask that subscriber afterwards.

ActorRuntime::shutdown_all flushes the broker before signalling anything, so broadcasting and then shutting down is not a race.

Reply

Builds the future a handler must return.

MethodUsed withDescription
Reply::ready()mutate_on, act_onComplete immediately, no async work
Reply::pending(future)mutate_on, act_onWrap an async block
Reply::try_pending(future)try_mutate_on, try_act_onWrap an async block returning Result
Reply::try_ok(value)try_mutate_on, try_act_onImmediate success
Reply::try_err(error)try_mutate_on, try_act_onImmediate failure

Handler futures require Send

As of v10, handler and lifecycle futures require Send + 'static, without Sync. Await third-party I/O directly inside Reply::pending or Reply::try_pending. Reusable handler closures, actor state, and messages retain their Sync requirements. Explicitly typed named handlers must remove Sync from their boxed future return signature. See Integration.

MessageContext

The second argument every handler receives — the message plus its routing information. Written ctx or envelope in examples.

MethodDescription
message()Reference to the message payload (an accessor, not a field)
reply_envelope()OutboundEnvelope addressed back to the sender
origin_envelope()OutboundEnvelope representing where the message came from
new_envelope(&address)OutboundEnvelope to a different recipient, keeping this actor as the return address

MessageContext is not exported from the prelude — you don't normally need to name it, since handlers are closures and the type is inferred.

OutboundEnvelope

A message prepared for sending. This is what reply_envelope() and create_envelope() hand you.

MethodDescription
send(msg).awaitSend a message via this envelope
try_send(msg).awaitSend, returning Err instead of waiting if the inbox is full
reply(msg)Synchronous send (discouraged — prefer send)
reply_to()The return address
recipient()The recipient address, if any

Macros

#[acton_actor]

Marks a struct as actor state.

#[acton_actor]
struct Counter {
    count: i32,
}

#[acton_message]

Marks a struct as a message.

#[acton_message]
struct Increment;

#[acton_message(ipc)]  // Enable IPC serialization
struct GetValue;

#[acton_main]

Sets up the async runtime.

#[acton_main]
async fn main() {
    let runtime = ActonApp::launch_async().await;
    // ...
}

Prelude

Import everything:

use acton_reactive::prelude::*;
CategoryItems
Macrosacton_actor, acton_message, acton_main
SystemActonApp, ActorRuntime, ActorConfig, Broker, BrokerRef, ParentRef
ActorsManagedActor, Idle, Started, ActorHandle
HandlersReply
Request/replyAskError, DEFAULT_ASK_TIMEOUT
MessagesBrokerRequest, BrokerRequestEnvelope, MessageAddress, OutboundEnvelope, ChildTerminated, SystemSignal, FlushBroadcasts, BroadcastsFlushed
SupervisionSupervisedChild, SupervisionStatus, SupervisionState, SupervisionError, Escalation, RestartPolicy, TerminationReason, SupervisionStrategy, SupervisionDecision, RestartLimiter, RestartLimiterConfig, RestartLimitExceeded, RestartStats, RestartGeneration, ChildIndex, BackoffDelay, MAX_SUPERVISION_DEPTH
Supervision eventsChildSupervised, ChildRestarted, SupervisionEscalated
TraitsActonMessage, ActorHandleInterface, Broadcaster, Request, Subscribable, Subscriber
Re-exportseverything from acton_ern, plus async_trait and tokio
IPC (ipc feature)IpcClient, IpcClientConfig, IpcConfig, IpcEnvelope, IpcError, IpcResponse, IpcListenerHandle, IpcListenerStats, IpcTypeRegistry, RemoteActorRef, ShutdownResult, and the RemoteRequest trait

Note that MessageContext is deliberately not in the prelude; handlers receive it as an inferred closure parameter.

Previous
Integration