Crate rsactor

Source
Expand description

§rsActor

A Lightweight Rust Actor Framework with Simple Yet Powerful Task Control

rsActor is a lightweight, Tokio-based actor framework in Rust focused on providing simple yet powerful task control. It prioritizes simplicity and efficiency for local, in-process actor systems while giving developers complete control over their actors’ execution lifecycle — define your own run_loop, control execution, control the lifecycle.

§Features

  • Asynchronous Actors: Actors run in their own asynchronous tasks.
  • Message Passing: Actors communicate by sending and receiving messages.
    • tell: Send a message without waiting for a reply (fire-and-forget).
    • tell_with_timeout: Send a message without waiting for a reply, with a specified timeout.
    • ask: Send a message and await a reply.
    • ask_with_timeout: Send a message and await a reply, with a specified timeout.
    • tell_blocking: Blocking version of tell for use in tokio::task::spawn_blocking tasks.
    • ask_blocking: Blocking version of ask for use in tokio::task::spawn_blocking tasks.
  • Actor Lifecycle with Simple Yet Powerful Task Control: Actors have on_start, on_stop, and run_loop lifecycle hooks. The distinctive run_loop feature provides a dedicated task execution environment that users can control with simple yet powerful primitives, unlike other actor frameworks. This gives developers complete control over their actor’s task logic while the framework manages the underlying execution.
  • 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.

§Core Concepts

  • Actor: Trait defining actor behavior and lifecycle hooks (on_start, on_stop, run_loop).
  • 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.
  • MailboxMessage(Internal): Enum for messages in an actor’s mailbox (user messages and control signals).
  • Runtime(Internal): Manages an actor’s internal lifecycle and message loop.

§Getting Started

Define an actor struct, implement Actor and Message<M> for each message type. Use impl_message_handler! to wire up message handling. All Actor lifecycle methods (on_start, on_stop, run_loop) are optional and have default implementations.

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

// 1. Define your actor struct
struct MyActor {
    data: String,
}

impl MyActor {
    fn new(data: &str) -> Self {
        MyActor { data: data.to_string() }
    }
}

// 2. Implement the Actor trait (on_start, on_stop, run_loop are optional)
impl Actor for MyActor {
    type Error = anyhow::Error; // Define an error type

    // Optional: Implement on_start for initialization
    // async fn on_start(&mut self, _actor_ref: &ActorRef) -> Result<(), Self::Error> {
    //     println!("MyActor (data: '{}') started!", self.data);
    //     Ok(())
    // }

    // Optional: Implement on_stop for cleanup
    // async fn on_stop(&mut self, _actor_ref: &ActorRef, _reason: &rsactor::ActorStopReason) -> Result<(), Self::Error> {
    //     println!("MyActor (data: '{}') stopped!", self.data);
    //     Ok(())
    // }

    // Optional: Implement run_loop for the actor's main execution logic.
    // This method is called after on_start. If it returns Ok(()), the actor stops normally.
    // If it returns Err(_), the actor stops due to an error.
    async fn run_loop(&mut self, _actor_ref: &ActorRef) -> Result<(), Self::Error> {
        let mut tick_300ms = tokio::time::interval(std::time::Duration::from_millis(300));
        let mut tick_1s = tokio::time::interval(std::time::Duration::from_secs(1));
        loop {
            tokio::select! {
                _ = tick_300ms.tick() => {
                    println!("Tick: 300ms");
                }
                _ = tick_1s.tick() => {
                    println!("Tick: 1s");
                }
            }
        }
        // If you add a break to exit the loop, return Ok(()) here to satisfy the function signature.
        // Ok(())
    }
}

// 3. Define your message types
struct GetData; // A message to get the actor's data
struct UpdateData(String); // A message to update the actor's data

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

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

impl Message<UpdateData> for MyActor {
    type Reply = (); // This message does not return a value

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

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

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

    // Send an "ask" message and wait for a reply
    let current_data: String = actor_ref.ask(GetData).await?;
    println!("Received data: {}", current_data);

    // Send a "tell" message (fire-and-forget)
    actor_ref.tell(UpdateData("new data".to_string())).await?;

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

    // Stop the actor gracefully
    actor_ref.stop().await?;

    // Wait for the actor to terminate
    let (_actor_instance, stop_reason) = join_handle.await?;
    println!("Actor stopped with reason: {:?}", stop_reason);

    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 a given actor type.

Structs§

ActorRef
A reference to an actor, allowing messages to be sent to it.

Enums§

ActorStopReason
Represents the reason an actor stopped.
Error

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’s Runtime.

Functions§

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

Type Aliases§

Result
A Result type specialized for rsactor operations.