Skip to content

hiking90/rsactor

Repository files navigation

rsActor

CI Crates.io Docs.rs Rust Version

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.

Note: This project is actively evolving. While core APIs are stable, some features may be refined in future releases.

Core Features

Actor System

  • Minimalist Design: Focuses on core actor model primitives with a clean API
  • Tokio-Native: Built for the tokio asynchronous runtime
  • Actor Derive Macro: #[derive(Actor)] for simple actors that don't need complex initialization

Message Passing

Method Description
ask / ask_with_timeout Send a message and asynchronously await a reply
tell / tell_with_timeout Send a message without waiting for a reply (fire-and-forget)
blocking_ask / blocking_tell Blocking versions for tokio::task::spawn_blocking contexts
  • Macro-Assisted Handlers: #[message_handlers] attribute macro with #[handler] method attributes for automatic message handling

Actor Lifecycle

Three well-defined hooks for managing actor behavior:

  • on_start: Initializes the actor's state (required)
  • on_run: Main execution logic, runs concurrently with message handling (optional)
  • on_stop: Cleanup before termination, with killed flag for graceful vs immediate (optional)

Supports graceful termination (stop()) and immediate termination (kill()), with ActorResult enum representing lifecycle outcomes.

Type Safety

  • Compile-Time Safety: ActorRef<T> ensures message handling consistency and prevents type-related runtime errors
  • Handler Traits: TellHandler<M> and AskHandler<M, R> enable unified management of different actor types in a single collection
  • Actor Control Traits: ActorControl and WeakActorControl provide type-erased lifecycle management
  • Only Send Required: Actor structs only need Send trait (not Sync), enabling interior mutability types like std::cell::Cell

Observability

  • Optional Tracing: Built-in support via tracing feature flag for actor lifecycle events, message handling, and performance metrics
  • Metrics Support: Optional metrics feature for monitoring message counts, processing times, and actor uptime

Why rsActor?

Focused Scope

Unlike broader frameworks like Actix, rsActor specializes exclusively in local, in-process actor systems. This focused approach eliminates complexity from unused features like remote actors or clustering, resulting in a cleaner API and smaller footprint.

Key Advantages

  • Simplicity First: Minimal API surface with sensible defaults
  • Type-Safe by Default: ActorRef<T> ensures compile-time message validation with zero runtime overhead
  • Flexible Type Erasure: Handler traits enable managing heterogeneous actor collections without sacrificing type safety
  • Production-Ready Observability: Integrated tracing and metrics support
  • Mutex-Free Design: No shared locks between actors - state is isolated within each actor

Getting Started

1. Add Dependency

[dependencies]
rsactor = "0.12" # Check crates.io for the latest version

# Optional: Enable tracing support for detailed observability
# rsactor = { version = "0.12", features = ["tracing"] }

For using the derive macros, you'll also need the message_handlers attribute macro which is included by default.

2. 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.

3. Choose Your Actor Creation Approach

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 message types
struct Increment;
struct GetCount;

// 2. Define your actor struct and derive Actor
#[derive(Actor)]
struct CounterActor {
    count: u32,
}

// 3. Use the #[message_handlers] macro with #[handler] attributes to automatically generate Message trait implementations
#[message_handlers]
impl CounterActor {
    #[handler]
    async fn handle_increment(&mut self, _msg: Increment, _: &ActorRef<Self>) {
        self.count += 1;
    }

    #[handler]
    async fn handle_get_count(&mut self, _msg: GetCount, _: &ActorRef<Self>) -> u32 {
        self.count
    }
}

// 4. Usage
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let actor = CounterActor { count: 0 };
    let (actor_ref, _join_handle) = spawn::<CounterActor>(actor);

    actor_ref.tell(Increment).await?;
    let count = actor_ref.ask(GetCount).await?;
    println!("Count: {}", count); // Prints: Count: 1

    actor_ref.stop().await?;
    Ok(())
}

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;
use tracing::info;

// Define actor struct
#[derive(Debug)] // Added Debug for printing the actor in ActorResult
struct CounterActor {
    count: u32,
}

// Implement Actor trait
impl Actor for CounterActor {
    type Args = u32; // Define an args type for actor creation
    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_count: Self::Args, actor_ref: &ActorRef<Self>) -> Result<Self, Self::Error> {
        info!("CounterActor (id: {}) started. Initial count: {}", actor_ref.identity(), initial_count);
        Ok(CounterActor {
            count: initial_count,
        })
    }
}

// Define message types
struct Increment(u32);

// Use message_handlers macro for message handling
#[message_handlers]
impl CounterActor {
    #[handler]
    async fn handle_increment(&mut self, msg: Increment, _actor_ref: &ActorRef<Self>) -> u32 {
        self.count += msg.0;
        self.count
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    tracing_subscriber::fmt().init(); // Initialize tracing

    info!("Creating CounterActor");

    let (actor_ref, join_handle) = spawn::<CounterActor>(0u32); // Pass initial count as Args
    info!("CounterActor spawned with ID: {}", actor_ref.identity());

    let new_count: u32 = actor_ref.ask(Increment(5)).await?;
    info!("Incremented count: {}", new_count);

    actor_ref.stop().await?;
    info!("Stop signal sent to CounterActor (ID: {})", actor_ref.identity());

    let actor_result = join_handle.await?;
    info!(
        "CounterActor (ID: {}) task completed. Result: {:?}",
        actor_ref.identity(),
        actor_result
    );

    Ok(())
}

Examples

rsActor comes with several examples that demonstrate various features and use cases:

Run any example with:

cargo run --example <example_name>

All examples support tracing when enabled with the tracing feature:

RUST_LOG=debug cargo run --example <example_name> --features tracing

Optional Features

Tracing Support

rsActor provides optional tracing support for comprehensive observability into actor behavior. When enabled, the framework 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)

To enable tracing support, add the tracing feature to your dependencies:

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

All examples include tracing support. Here's the recommended initialization pattern:

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize tracing subscriber
    tracing_subscriber::fmt()
        .with_max_level(tracing::Level::DEBUG)
        .with_target(false)
        .init();

    // Your actor code here...
    Ok(())
}

Run any example with tracing enabled:

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

Handler Traits

Handler traits (TellHandler, AskHandler, WeakTellHandler, WeakAskHandler) enable unified management of different actor types handling the same message in a single collection. See the Handler Traits Documentation for details.

Actor Control Traits

Actor control traits (ActorControl, WeakActorControl) provide type-erased lifecycle management for different actor types in a single collection. Handler traits provide as_control() and as_weak_control() methods to access lifecycle operations.

Documentation

Contributing

We welcome contributions! Here's how to get started:

Development Setup

git clone https://github.com/hiking90/rsactor.git
cd rsactor

# Run tests
cargo test --all-features

# Run examples
cargo run --example basic

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

Code Quality

Before submitting a PR, ensure:

cargo fmt                                                    # Format code
cargo clippy --all-targets --all-features -- -D warnings    # Lint check
cargo test --all-features                                    # All tests pass

Ways to Contribute

  • Bug reports and fixes
  • Documentation improvements
  • New examples
  • Performance optimizations
  • Feature requests

Claude Code Skills

rsActor provides Claude Code skills to help AI assistants write correct rsactor code.

Installation

# Global installation (recommended)
curl -sSL https://raw.githubusercontent.com/hiking90/rsactor/main/install-skills.sh | bash

# Project-local installation
curl -sSL https://raw.githubusercontent.com/hiking90/rsactor/main/install-skills.sh | bash -s -- --local

Available Skills

  • rsactor-actor: Create new actors with proper patterns
  • rsactor-handler: Add message handlers to existing actors
  • rsactor-guide: API reference and troubleshooting guide

License

This project is licensed under the Apache License 2.0. See the LICENSE-APACHE file for details.

About

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

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages