use anyhow::Error as AnyError;
use rsactor::{message_handlers, spawn, Actor, ActorRef, ActorWeak, Result};
use std::time::Duration;
use tokio::time::sleep;
use tracing::info;
#[derive(Debug)]
struct PingActor {
name: String,
ping_count: usize,
}
impl Actor for PingActor {
type Args = String;
type Error = AnyError;
async fn on_start(
args: Self::Args,
actor_ref: &ActorRef<Self>,
) -> std::result::Result<Self, Self::Error> {
info!(
"PingActor '{}' (id: {}) started!",
args,
actor_ref.identity()
);
Ok(PingActor {
name: args,
ping_count: 0,
})
}
async fn on_stop(
&mut self,
actor_ref: &ActorWeak<Self>,
killed: bool,
) -> std::result::Result<(), Self::Error> {
let status = if killed { "killed" } else { "stopped" };
info!(
"PingActor '{}' (id: {}) {} after {} pings",
self.name,
actor_ref.identity(),
status,
self.ping_count
);
Ok(())
}
}
#[derive(Debug)]
struct Ping;
#[derive(Debug)]
struct GetStatus;
#[derive(Debug)]
struct Status {
name: String,
ping_count: usize,
}
#[message_handlers]
impl PingActor {
#[handler]
async fn handle_ping(&mut self, _msg: Ping, _: &ActorRef<Self>) -> String {
self.ping_count += 1;
format!("{} pong! (count: {})", self.name, self.ping_count)
}
#[handler]
async fn handle_get_status(&mut self, _msg: GetStatus, _: &ActorRef<Self>) -> Status {
Status {
name: self.name.clone(),
ping_count: self.ping_count,
}
}
}
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.with_target(false)
.init();
println!("=== UntypedActorWeak Demo ===\n");
let (actor_ref, join_handle) = spawn::<PingActor>("TestActor".to_string());
let weak_ref = ActorRef::downgrade(&actor_ref);
println!(
"1. Created weak reference to actor: {}",
weak_ref.identity()
);
println!(" Weak reference is_alive: {}", weak_ref.is_alive());
if let Some(strong_ref) = weak_ref.upgrade() {
println!("2. Successfully upgraded weak reference to strong reference");
let response: String = strong_ref.ask(Ping).await?;
println!(" Response: {response}");
} else {
println!("2. Failed to upgrade weak reference - actor is dead");
}
let response1: String = actor_ref.ask(Ping).await?;
let response2: String = actor_ref.ask(Ping).await?;
println!("3. Sent more pings: '{response1}', '{response2}'");
if let Some(strong_ref) = weak_ref.upgrade() {
let status: Status = strong_ref.ask(GetStatus).await?;
println!(
"4. Actor status via weak ref: {} has {} pings",
status.name, status.ping_count
);
}
println!("5. Dropping strong actor reference...");
drop(actor_ref);
sleep(Duration::from_millis(100)).await;
println!("6. Checking weak reference after dropping strong reference:");
println!(" Weak reference is_alive: {}", weak_ref.is_alive());
if let Some(strong_ref) = weak_ref.upgrade() {
println!(" Successfully upgraded weak reference after drop");
match strong_ref.ask(Ping).await {
Ok(response) => println!(" Response: {response}"),
Err(e) => println!(" Error sending message: {e}"),
}
} else {
println!(" Failed to upgrade weak reference - actor is no longer available");
}
println!("7. Stopping actor...");
if let Ok(actor_result) = join_handle.await {
println!(" Actor stopped with result: {actor_result:?}");
}
println!("8. Final weak reference check:");
println!(" Weak reference is_alive: {}", weak_ref.is_alive());
if let Some(_strong_ref) = weak_ref.upgrade() {
println!(" Unexpected: weak reference can still be upgraded after actor termination");
} else {
println!(" Expected: weak reference cannot be upgraded after actor termination");
}
println!("\n=== Demo Complete ===");
Ok(())
}