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.
  • 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: The impl_message_handler! macro simplifies handling multiple message types.
  • Type Safety Features: Two actor reference types provide different levels of type safety:
    • ActorRef<T>: Compile-time type safety with zero runtime overhead (recommended)
    • UntypedActorRef: Runtime type handling for collections and dynamic scenarios

§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.
  • MessageHandler: Trait for type-erased message handling. This is typically implemented automatically by the impl_message_handler! macro.
  • ActorResult: Enum representing the outcome of an actor’s lifecycle (e.g., completed, failed).

§Getting Started

Define an actor struct, implement Actor and Message<M> for each message type, then use impl_message_handler! to wire up message handling.

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

// 1. Define your actor struct
#[derive(Debug)]
struct MyActor {
    data: String,
    tick_300ms: tokio::time::Interval,
    tick_1s: tokio::time::Interval,
}

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

    async fn on_start(args: Self::Args, _actor_ref: &ActorRef<Self>) -> std::result::Result<Self, Self::Error> {
        println!("MyActor (data: '{}') started!", args);
        Ok(MyActor {
            data: args,
            tick_300ms: tokio::time::interval(std::time::Duration::from_millis(300)),
            tick_1s: tokio::time::interval(std::time::Duration::from_secs(1)),
        })
    }

    async fn on_run(&mut self, _actor_ref: &ActorRef<Self>) -> Result<(), Self::Error> {
        tokio::select! {
            _ = self.tick_300ms.tick() => {
                println!("Tick: 300ms");
            }
            _ = self.tick_1s.tick() => {
                println!("Tick: 1s");
            }
        }
        Ok(())
    }
}

// 3. Define message types
struct GetData;
struct UpdateData(String);

// 4. Implement Message<M> for each message type
impl Message<GetData> for MyActor {
    type Reply = String;

    async fn handle(&mut self, _msg: GetData, _actor_ref: &ActorRef<Self>) -> Self::Reply {
        self.data.clone()
    }
}

impl Message<UpdateData> for MyActor {
    type Reply = ();

    async fn handle(&mut self, msg: UpdateData, _actor_ref: &ActorRef<Self>) -> Self::Reply {
        self.data = msg.0;
        println!("MyActor data updated!");
    }
}

// 5. Use the macro to implement MessageHandler
impl_message_handler!(MyActor, [GetData, UpdateData]);

#[tokio::main]
async fn main() -> Result<()> {
    let (actor_ref, join_handle) = spawn::<MyActor>("initial data".to_string());

    // Send messages
    let current_data: String = actor_ref.ask(GetData).await?;
    println!("Received data: {}", current_data);

    actor_ref.tell(UpdateData("new data".to_string())).await?;

    let updated_data: String = actor_ref.ask(GetData).await?;
    println!("Updated data: {}", updated_data);

    // Stop the actor
    actor_ref.stop().await?;
    let actor_result = join_handle.await?;
    println!("Actor stopped with result: {:?}", actor_result);

    Ok(())
}

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

Macros§

impl_message_handler
Implements the MessageHandler trait for both generic and non-generic actor types.

Structs§

ActorRef
A type-safe reference to an actor of type T.
Identity
UntypedActorRef
A type-erased reference to an actor, allowing messages to be sent to it without type safety.

Enums§

ActorResult
Result type returned when an actor’s lifecycle completes.
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.
Message
A trait for messages that an actor can handle, defining the reply type.
MessageHandler
A trait for type-erased message handling within the actor system.

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.

Type Aliases§

Result
A Result type specialized for rsactor operations.