Reference

Cheatsheet

Quick reference for common patterns. Copy, paste, and adapt.

Basic Actor Setup

use acton_reactive::prelude::*;

#[acton_actor]
struct MyActor {
    // your state here
}

#[acton_message]
struct MyMessage;

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

    let mut builder = runtime.new_actor::<MyActor>();
    builder.mutate_on::<MyMessage>(|actor, _envelope| {
        // handle message
        Reply::ready()
    });

    let handle = builder.start().await;

    handle.send(MyMessage).await;
    runtime.shutdown_all().await.ok();
}

Message Patterns

Fire-and-Forget

handle.send(DoSomething).await;

Request-Response (Reply Envelope)

// Server actor
builder.act_on::<GetValue>(|actor, envelope| {
    let value = actor.model.value;
    let reply = envelope.reply_envelope();
    Reply::pending(async move {
        reply.send(ValueResponse(value)).await;
    })
});

// Client actor receives response
client.mutate_on::<ValueResponse>(|actor, envelope| {
    let value = envelope.message().0;
    println!("Got: {}", value);
    Reply::ready()
});

Broadcast

// Publisher
let broker = runtime.broker();
broker.broadcast(Event { data: "hello".into() }).await;

// Subscriber (before starting)
builder.mutate_on::<Event>(|actor, envelope| {
    println!("Got: {}", envelope.message().data);
    Reply::ready()
});
builder.handle().subscribe::<Event>().await;
let handle = builder.start().await;

Handler Patterns

Mutate State

builder.mutate_on::<Increment>(|actor, _envelope| {
    actor.model.count += 1;
    Reply::ready()
});

Read State with Reply

builder.act_on::<GetCount>(|actor, envelope| {
    let count = actor.model.count;
    let reply = envelope.reply_envelope();
    Reply::pending(async move {
        reply.send(CountResponse(count)).await;
    })
});

Async Handler

builder.act_on::<FetchData>(|_actor, envelope| {
    let url = envelope.message().url.clone();
    let reply = envelope.reply_envelope();
    Reply::pending(async move {
        let resp = reqwest::get(&url).await.unwrap();
        let body = resp.text().await.unwrap();
        reply.send(FetchResponse { body }).await;
    })
});

Child Actors

Create and Supervise Child

builder.mutate_on::<SpawnChild>(|actor, _envelope| {
    let mut child = actor.create_child("worker".to_string())
        .expect("Failed to create child");
    child.mutate_on::<Task>(handle_task);

    let parent_handle = actor.handle().clone();
    Reply::pending(async move {
        let child_handle = parent_handle.supervise(child).await
            .expect("Failed to supervise");
        println!("Child started: {}", child_handle.id());
    })
});

Access Children

for child in actor.children().iter() {
    child.send(Ping).await;
}

Lifecycle Hooks

Before Start

builder.before_start(|actor| async move {
    println!("Actor starting!");
});

After Stop

builder.after_stop(|actor| async move {
    println!("Actor stopped");
});

Common State Patterns

With Default

#[acton_actor]
struct Counter {
    count: i32,  // defaults to 0
}

With Custom Default

#[acton_actor]
#[derive(Default)]
struct Config {
    timeout: Duration,
}

impl Default for Config {
    fn default() -> Self {
        Self { timeout: Duration::from_secs(30) }
    }
}

With External Resources

#[acton_actor]
struct DbActor {
    pool: Option<PgPool>,
}

// Initialize via message or before_start

Error Handling

In Handlers

builder.mutate_on::<RiskyOp>(|actor, envelope| {
    match do_risky_thing() {
        Ok(result) => {
            actor.model.data = result;
            Reply::ready()
        }
        Err(e) => {
            tracing::error!("Failed: {}", e);
            Reply::ready()  // Actor continues
        }
    }
});

Signal Errors to Other Actors

builder.mutate_on::<Query>(|actor, envelope| {
    let reply = envelope.reply_envelope();
    match do_query() {
        Ok(data) => Reply::pending(async move {
            reply.send(QuerySuccess(data)).await;
        }),
        Err(e) => Reply::pending(async move {
            reply.send(QueryFailed(e.to_string())).await;
        }),
    }
});

Testing

Basic Test

#[tokio::test]
async fn test_actor() {
    let mut runtime = ActonApp::launch_async().await;

    let mut counter = runtime.new_actor::<Counter>();
    counter
        .mutate_on::<Increment>(|actor, _env| {
            actor.model.count += 1;
            Reply::ready()
        });

    let handle = counter.start().await;
    handle.send(Increment).await;

    // Use probe actor or atomic counter to verify
    tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;

    runtime.shutdown_all().await.ok();
}

Quick Imports

// Everything you need
use acton_reactive::prelude::*;

// For IPC
use acton_reactive::ipc::{IpcEnvelope, IpcConfig};
use acton_reactive::ipc::protocol::{write_envelope, read_response};

Common Mistakes

WrongRight
ActonApp::launch()ActonApp::launch_async().await
handle.ask(msg)Use reply envelope pattern
Reply::with(value)Use Reply::pending + reply envelope
Forgetting .await on start()builder.start().await
Mutating in act_onUse mutate_on for state changes
Using _msg parameterUse envelope.message()
Previous
Examples