Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Tell (Fire and Forget)

The “tell” pattern, accessed via actor_ref.tell(message), is used to send a message to an actor without expecting a direct reply. This is a form of asynchronous, one-way communication.

Characteristics of tell:

  • Asynchronous: actor_ref.tell(message) returns a Future that completes once the message is enqueued into the actor’s mailbox. It does not wait for the actor to process the message.
  • No Direct Reply: The sender does not receive a value back from the actor.
  • Reply Type: For messages intended for tell, their handler typically returns ().
  • Error Handling: Returns Result<(), rsactor::Error>. An error occurs if the actor is no longer alive.

Usage Example:

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

#[derive(Actor)]
struct LoggerActor;

// Message to log a string
struct LogMessage(String);

#[message_handlers]
impl LoggerActor {
    #[handler]
    async fn handle_log(&mut self, msg: LogMessage, actor_ref: &ActorRef<Self>) -> () {
        info!("LoggerActor (id: {}): {}", actor_ref.identity(), msg.0);
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    tracing_subscriber::fmt::init();
    let (logger_ref, jh) = spawn::<LoggerActor>(LoggerActor);

    // Send a log message using tell
    logger_ref.tell(LogMessage("Application started successfully".to_string())).await?;
    logger_ref.tell(LogMessage("Processing item #123".to_string())).await?;

    logger_ref.stop().await?;
    jh.await?;
    Ok(())
}

tell_with_timeout

rsActor also provides actor_ref.tell_with_timeout(message, timeout).

  • Similar to tell, but allows specifying a Duration as a timeout for enqueuing the message.
  • Returns a Timeout error if the message cannot be enqueued within the given duration.
#![allow(unused)]
fn main() {
use std::time::Duration;

match logger_ref.tell_with_timeout(
    LogMessage("Critical event".to_string()),
    Duration::from_millis(100)
).await {
    Ok(_) => info!("Log message sent."),
    Err(e) => info!("Failed to send log message: {:?}", e),
}
}

blocking_tell

For sending messages from non-async contexts (e.g., spawn_blocking tasks):

#![allow(unused)]
fn main() {
// Without timeout (most efficient)
actor_ref.blocking_tell(LogMessage("from blocking".into()), None)?;

// With timeout
actor_ref.blocking_tell(LogMessage("from blocking".into()), Some(Duration::from_secs(1)))?;
}

When to Use tell:

  • Notifications/Events: When an actor needs to notify another without needing an immediate response.
  • Commands: Issuing commands where the sender doesn’t need to wait for completion.
  • Decoupling: When you want to minimize coupling between actors.
  • Avoiding Deadlocks: In complex actor interactions, tell can break circular dependency cycles that ask might create.

tell is a fundamental communication pattern for building reactive and event-driven systems with actors.