Actors
Actors own state and run one handler at a time, including across suspension.
A call waits for completion. A void handler still returns a completion
outcome; use match, ? or handle to deal with failure.
The message-passing concepts guide
develops the isolation model before this page's syntax and delivery details.
actor Counter {
var count: i64 = 0;
receive fn increment(n: i64) { count = count + n; }
receive fn total() -> i64 { count }
}
fn main() {
let counter = spawn Counter;
counter.increment(42) handle error {
println(f"increment failed: {error}");
return;
};
match counter.total() {
.Ok(value) => println(value),
.Err(error) => println(f"query failed: {error}"),
}
stop(counter);
stopped(counter);
}
fork counter.total() creates a task; await task joins its result.
mailbox(target, on_full: ...) submits without waiting for completion and
refuses value-returning handlers. policy(target, on_full: .Wait) or the .Reject policy
controls admission for completion calls; a bare handle waits for room.
Completion errors are ActorError<E, Req>. A rejecting completion view or
mailbox submission returns Rejected(SendFailure<Req>) only when the request
was definitely not admitted. Its message supports retry() and to(target).
Other errors can leave remote admission or completion uncertain and do not
return a request to retry. Remote mailbox success acknowledges destination
admission, not handler completion.
Isolation and backpressure
Messages must satisfy Send. Ordinary value data is snapshotted at the actor boundary, while affine owners such as resources and stream halves move. Actor state is accessed only by that actor's handlers, and each actor runs one handler at a time.
An actor without a mailbox declaration has unbounded capacity.
mailbox 1024; sets a bound and defaults to waiting for space. Declaration
policies include drop_new, drop_old, fail, and
coalesce(key) fallback .... Coalescing applies only to submissions for a
unit-returning handler and replaces older queued work with the same handler
and key. Completion calls are never discarded or superseded.
A fails handler returns Failed(E) to a completion caller. When submitted
through a mailbox view, its error faults the actor with its Display text.
stop(actor) requests a graceful stop; stopped(actor) waits for terminal
cleanup. terminate(actor) requests cancellation. Inside an
actor, self is its handle and self.field accesses state. An actor's
handle has that actor's type T.
Periodic handlers
#[every(duration)] schedules a zero-argument handler through the actor's
normal mailbox. It starts with the actor and stops when the actor stops, so
periodic work never runs concurrently with another handler on that actor.
actor Heartbeat {
var ticks: i64 = 0;
#[every(1s)]
receive fn tick() { ticks += 1; }
receive fn count() -> i64 { ticks }
}
A periodic handler has no parameters and unit success. On WASI its timer is cooperative; see target capabilities.
Lambda actors
A lambda actor handles one message type. Its call waits for completion like
a named actor call, and its handle type is actor(Message) -> Reply.
fn main() {
let double: actor(i64) -> i64 = actor |value: i64| -> i64 {
value * 2
};
match double(21) {
.Ok(value) => println(value),
.Err(error) => println(f"worker failed: {error}"),
}
stop(double);
stopped(double);
}
actor move |value| { ... } requests transfer of captured owners. Ordinary
value captures already become independent actor state. Use a named actor for
several handlers, recursive protocols, trait implementation, or a named
supervisor child.
Interceptors
One #[intercept] method can inspect each receive delivery around the
handler. It consumes its Call<Self> exactly once with proceed() or
refuse(reason) and returns the resulting Outcome<Self>. Supervisor
interceptors wrap child interceptors from the outside in. Refusal is an
actor error for completion calls and does not crash the actor.
Interceptors cover receive handlers, stream starts, and periodic ticks. Lifecycle hooks, system messages, replies, and stream items bypass them.
Supervised children and admitted remote actors also have the actor's own type.
A supervised reference follows its stable role through restarts. is compares
role identity for roles and exact incarnation identity for direct or imported
references; a role differs from its current occupant. restarted(actor) waits
only for an already pending role replacement to settle, returning immediately
for direct and imported references without asserting liveness.