Core Concepts
The Actor System
The actor runtime brings your actors to life. It spawns actors, manages their lifecycles, provides handles for communication, and coordinates shutdown.
Creating the Runtime
Every Acton application starts by launching the runtime:
use acton_reactive::prelude::*;
#[acton_main]
async fn main() {
let mut runtime = ActonApp::launch_async().await;
// Create actors, send messages...
runtime.shutdown_all().await.ok();
}
Creating Actors
Once you have a runtime, create actors:
let mut counter = runtime.new_actor::<Counter>();
counter
.mutate_on::<Increment>(handle_increment)
.act_on::<GetCount>(handle_get);
let handle = counter.start().await;
The start() method:
- Creates the actor instance
- Sets up its message queue
- Starts it running on the async runtime
- Returns a handle for sending messages
Named Actors
Give actors meaningful names for easier debugging:
let mut counter = runtime.new_actor_with_name::<Counter>("user-counter".to_string());
Working with Handles
A handle is your interface to an actor:
let handle = counter.start().await;
// Fire and forget
handle.send(Increment).await;
Handles are cheap to clone:
let handle_for_web = handle.clone();
let handle_for_background = handle.clone();
The Broker: Pub/Sub
Not all communication is point-to-point. The Broker provides publish/subscribe messaging.
Getting the Broker
let broker = runtime.broker();
Publishing Events
#[acton_message]
struct UserLoggedIn { user_id: u64 }
broker.broadcast(UserLoggedIn { user_id: 123 }).await;
Subscribing to Events
Subscribe actors before starting them:
let mut listener = runtime.new_actor::<EventListener>();
listener.mutate_on::<UserLoggedIn>(|actor, envelope| {
let msg = envelope.message();
println!("User {} logged in", msg.user_id);
Reply::ready()
});
// Subscribe before starting
listener.handle().subscribe::<UserLoggedIn>().await;
let handle = listener.start().await;
Now the actor receives all UserLoggedIn events broadcast through the broker.
Broker vs Direct Messaging
Use handles when:
- You know exactly which actor should receive the message
- You're doing point-to-point communication
Use the broker when:
- Multiple actors should react to an event
- The sender doesn't know (or care) who receives it
Shutdown
Proper shutdown ensures messages aren't lost:
runtime.shutdown_all().await.ok();
For signal handling:
use tokio::signal;
// Wait for Ctrl+C
signal::ctrl_c().await.expect("Failed to listen");
runtime.shutdown_all().await.ok();
Next
Supervision Basics — Handling failures gracefully