Skip to main content

Crate rsactor

Crate rsactor 

Source
Expand description

§rsActor

A Simple and Efficient In-Process Actor Model Implementation for Rust

rsActor is a lightweight, Tokio-based actor framework in Rust focused on providing a simple and efficient actor model for local, in-process systems. It emphasizes clean message-passing semantics and straightforward actor lifecycle management while maintaining high performance for Rust applications.

§Features

  • Asynchronous Actors: Actors run in their own asynchronous tasks.
  • Message Passing: Actors communicate by sending and receiving messages.
  • Priority Channel (opt-in via SpawnOptions::with_priority): A dedicated mpsc channel of fixed capacity 1 that the runtime polls with higher priority than the regular mailbox but lower priority than the kill() (terminate signal). Use it for short, infrequent control messages such as health checks and pause/resume. Send via tell_priority / ask_priority. The priority channel is off by default; calls on a non-priority actor return Error::PriorityChannelNotEnabled.
  • Straightforward Actor Lifecycle: Actors have on_start, on_run, and on_stop lifecycle hooks that provide a clean and intuitive actor lifecycle management system. The framework manages the execution flow while giving developers full control over actor behavior.
  • Graceful Shutdown & Kill: Actors can be stopped gracefully or killed immediately.
  • Typed Messages: Messages are strongly typed, and replies are also typed.
  • Macro for Message Handling:
    • message_handlers attribute macro with #[handler] method attributes for automatic message handling (recommended)
  • Type Safety Features: ActorRef<T> provides compile-time type safety with zero runtime overhead
  • Optional Tracing Support: Built-in observability using the tracing crate (enable with tracing feature):
    • Actor lifecycle event tracing (start, stop, different termination scenarios)
    • Message handling with timing and performance metrics
    • Reply processing and error handling tracing
    • Structured, non-redundant logs for easier debugging and monitoring
  • Dead Letter Tracking: Automatic logging of undelivered messages via DeadLetterReason:
    • All failed message deliveries are logged with actor and message type information
    • Helps identify stopped actors, timeouts, and dropped replies
    • Zero overhead on successful message delivery (hot path optimization)
  • Enhanced Error Debugging: Rich error information via Error::debugging_tips() and Error::is_retryable():
    • Actionable debugging tips for each error type
    • Retry classification for timeout errors

§Core Concepts

  • Actor: Trait defining actor behavior and lifecycle hooks (on_start required, on_run optional).
  • Message<M>: Trait for handling a message type M and defining its reply type.
  • ActorRef: Handle for sending messages to an actor.
  • spawn: Function to create and start an actor, returning an ActorRef and a JoinHandle.
  • ActorResult: Enum representing the outcome of an actor’s lifecycle (e.g., completed, failed).

§Getting Started

§Message Handling with #[message_handlers]

rsActor uses the #[message_handlers] attribute macro combined with #[handler] method attributes for message handling. This is required for all actors and offers several advantages:

  • Selective Processing: Only methods marked with #[handler] are treated as message handlers.
  • Clean Separation: Regular methods can coexist with message handlers within the same impl block.
  • Automatic Generation: The macro automatically generates the necessary Message trait implementations and handler registrations.
  • Type Safety: Message handler signatures are verified at compile time.
  • Reduced Boilerplate: Eliminates the need to manually implement Message traits.

§Option A: Simple Actor with #[derive(Actor)]

For simple actors that don’t need complex initialization logic, use the #[derive(Actor)] macro:

use rsactor::{Actor, ActorRef, message_handlers, spawn};

// 1. Define your actor struct and derive Actor
#[derive(Actor)]
struct MyActor {
    name: String,
    count: u32,
}

// 2. Define message types
struct GetName;
struct Increment;

// 3. Use message_handlers macro with handler attributes
#[message_handlers]
impl MyActor {
    #[handler]
    async fn handle_get_name(&mut self, _msg: GetName, _: &ActorRef<Self>) -> String {
        self.name.clone()
    }

    #[handler]
    async fn handle_increment(&mut self, _msg: Increment, _: &ActorRef<Self>) -> () {
        self.count += 1;
    }

    // Regular methods can coexist without the #[handler] attribute
    fn get_count(&self) -> u32 {
        self.count
    }
}

// 4. Usage
let actor_instance = MyActor { name: "Test".to_string(), count: 0 };
let (actor_ref, _join_handle) = spawn::<MyActor>(actor_instance);

let name = actor_ref.ask(GetName).await?;
actor_ref.tell(Increment).await?;

§Option B: Custom Actor Implementation with Manual Initialization

For actors that need custom initialization logic, implement the Actor trait manually:

use rsactor::{Actor, ActorRef, message_handlers, spawn};
use anyhow::Result;

// 1. Define your actor struct
#[derive(Debug)] // Added Debug for printing the actor in ActorResult
struct MyActor {
    data: String,
    count: u32,
}

// 2. Implement the Actor trait manually
impl Actor for MyActor {
    type Args = String;
    type Error = anyhow::Error;

    // on_start is required and must be implemented.
    // on_run and on_stop are optional and have default implementations.
    async fn on_start(initial_data: Self::Args, actor_ref: &ActorRef<Self>) -> std::result::Result<Self, Self::Error> {
        println!("MyActor (id: {}) started with data: '{}'", actor_ref.identity(), initial_data);
        Ok(MyActor {
            data: initial_data,
            count: 0,
        })
    }
}

// 3. Define message types
struct GetData;
struct IncrementMsg(u32);

// 4. Use message_handlers macro for message handling
#[message_handlers]
impl MyActor {
    #[handler]
    async fn handle_get_data(&mut self, _msg: GetData, _actor_ref: &ActorRef<Self>) -> String {
        self.data.clone()
    }

    #[handler]
    async fn handle_increment(&mut self, msg: IncrementMsg, _actor_ref: &ActorRef<Self>) -> u32 {
        self.count += msg.0;
        self.count
    }
}

// 5. Usage
let (actor_ref, join_handle) = spawn::<MyActor>("initial data".to_string());

let current_data: String = actor_ref.ask(GetData).await?;
let new_count: u32 = actor_ref.ask(IncrementMsg(5)).await?;

actor_ref.stop().await?;
let actor_result = join_handle.await?;

Both approaches also work with enums, making it easy to create state machine actors:

use rsactor::{Actor, ActorRef, message_handlers, spawn};

// Using message_handlers macro approach
#[derive(Actor, Clone)]
enum StateActor {
    Idle,
    Processing(String),
    Completed(i32),
}

struct GetState;
struct StartProcessing(String);
struct Complete(i32);

#[message_handlers]
impl StateActor {
    #[handler]
    async fn handle_get_state(&mut self, _msg: GetState, _: &ActorRef<Self>) -> StateActor {
        self.clone()
    }

    #[handler]
    async fn handle_start_processing(&mut self, msg: StartProcessing, _: &ActorRef<Self>) -> () {
        *self = StateActor::Processing(msg.0);
    }

    #[handler]
    async fn handle_complete(&mut self, msg: Complete, _: &ActorRef<Self>) -> () {
        *self = StateActor::Completed(msg.0);
    }
}

§Tracing Support

rsActor provides optional tracing support for comprehensive observability. Enable it with the tracing feature:

[dependencies]
rsactor = { version = "0.15", features = ["tracing"] }
tracing = "0.1"
tracing-subscriber = "0.3"

When enabled, rsActor emits structured trace events for:

  • Actor lifecycle events (start, stop, termination scenarios)
  • Message sending and handling with timing information
  • Reply processing and error handling
  • Performance metrics (message processing duration)

All examples support tracing. Here’s the integration pattern:

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize tracing subscriber to see logs
    // The `tracing` crate is always available for logging
    tracing_subscriber::fmt()
        .with_max_level(tracing::Level::DEBUG)
        .with_target(false)
        .init();

    // Your existing actor code here...
    // Logs are automatically emitted via tracing::warn!, tracing::error!, etc.
    Ok(())
}

Run any example with debug logging:

RUST_LOG=debug cargo run --example basic

Enable instrumentation spans with the tracing feature:

RUST_LOG=debug cargo run --example basic --features tracing

This crate-level documentation provides an overview of rsActor. For more details on specific components, please refer to their individual documentation.

Structs§

ActorRef
A type-safe reference to an actor of type T.
ActorWeak
A weak, type-safe reference to an actor of type T.
Identity
SpawnOptions
Configuration options for spawning an actor.

Enums§

ActorResult
Result type returned when an actor’s lifecycle completes.
DeadLetterReason
Reason why a message became a dead letter.
Error
Represents errors that can occur in the rsactor framework.
FailurePhase
Represents the phase during which an actor failure occurred.

Constants§

DEFAULT_MAILBOX_CAPACITY
The default mailbox capacity for actors.

Traits§

Actor
Defines the behavior of an actor.
ActorControl
Type-erased trait for actor lifecycle control with strong references.
AskHandler
Request-response message handler for strong references (object-safe).
Message
A trait for messages that an actor can handle, defining the reply type.
TellHandler
Fire-and-forget message handler for strong references (object-safe).
WeakActorControl
Type-erased trait for actor lifecycle control with weak references.
WeakAskHandler
Weak handler for request-response messages (object-safe).
WeakTellHandler
Weak handler for fire-and-forget messages (object-safe).

Functions§

set_default_mailbox_capacity
Sets the global default buffer size for actor mailboxes.
spawn
Spawns a new actor and returns an ActorRef<T> to it, along with a JoinHandle.
spawn_with_mailbox_capacity
Spawns a new actor with a specified mailbox capacity and returns an ActorRef<T> to it, along with a JoinHandle.
spawn_with_options
Spawns a new actor with the given SpawnOptions and returns an ActorRef<T> along with a JoinHandle.

Type Aliases§

BoxFuture
A boxed future that is Send and can be stored in collections.
Result
A Result type specialized for rsactor operations.

Attribute Macros§

message_handlers
Attribute macro for automatically generating Message trait implementations from method definitions.

Derive Macros§

Actor
Derive macro for automatically implementing the Actor trait.