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 aFuturethat 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.
ReplyType: For messages intended fortell, 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 aDurationas a timeout for enqueuing the message. - Returns a
Timeouterror 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,
tellcan break circular dependency cycles thataskmight create.
tell is a fundamental communication pattern for building reactive and event-driven systems with actors.