### Tokio Example: Basic Actor Implementation and Usage with xtra Source: https://github.com/restioson/xtra/blob/master/README.md This Rust code demonstrates a basic actor implementation using xtra with the Tokio runtime. It defines a 'Printer' actor that counts and prints messages. The example shows how to spawn the actor, send messages to it, and handle responses using async/await syntax. ```rust use xtra::prelude::*; #[derive(Default, xtra::Actor)] struct Printer { times: usize, } struct Print(String); impl Handler for Printer { type Return = (); async fn handle(&mut self, print: Print, _ctx: &mut Context) { self.times += 1; println!("Printing {}. Printed {} times so far.", print.0, self.times); } } #[tokio::main] async fn main() { let addr = xtra::spawn_tokio(Printer::default(), Mailbox::unbounded()); loop { addr.send(Print("hello".to_string())) .await .expect("Printer should not be dropped"); } } ``` -------------------------------- ### Spawn Functions - Runtime-Specific Actor Spawning with xtra Source: https://context7.com/restioson/xtra/llms.txt Provides convenience functions for spawning actors on different asynchronous runtimes, including `spawn_tokio`, `spawn_async_std`, `spawn_smol`, and `spawn_wasm_bindgen`. This allows for flexible actor deployment across various environments. The examples show basic actor setup and message handling. ```rust use xtra::prelude::*; #[derive(Default, xtra::Actor)] struct MyActor; struct Ping; impl Handler for MyActor { type Return = &'static str; async fn handle(&mut self, _: Ping, _ctx: &mut Context) -> &'static str { "pong" } } // Tokio example #[tokio::main] async fn main() { // Using spawn_tokio let addr = xtra::spawn_tokio(MyActor::default(), Mailbox::unbounded()); println!("{}", addr.send(Ping).await.unwrap()); // pong } // async-std example (requires async_std feature) // #[async_std::main] // async fn main() { // let addr = xtra::spawn_async_std(MyActor::default(), Mailbox::unbounded()); // println!("{}", addr.send(Ping).await.unwrap()); // } // smol example (requires smol feature) // fn main() { // smol::block_on(async { // let addr = xtra::spawn_smol(MyActor::default(), Mailbox::unbounded()); // println!("{}", addr.send(Ping).await.unwrap()); // }) // } ``` -------------------------------- ### Address Sink - Stream Integration with xtra::Address Source: https://context7.com/restioson/xtra/llms.txt Demonstrates how to convert an `xtra::Address` into a `futures_util::Sink` for seamless integration with stream processing. This feature requires the `sink` feature to be enabled and handlers to have a return type of `()`. The example shows forwarding a stream of messages to an actor for logging. ```rust use xtra::prelude::*; use futures_util::StreamExt; use futures_util::stream; use std::time::Duration; #[derive(Default, xtra::Actor)] struct Logger; struct LogMessage(String); impl Handler for Logger { type Return = (); // Must be () for sink conversion async fn handle(&mut self, LogMessage(msg): LogMessage, _ctx: &mut Context) { println!("Log: {}", msg); } } #[tokio::main] async fn main() { let addr = xtra::spawn_tokio(Logger::default(), Mailbox::unbounded()); // Convert address to sink let sink = addr.clone().into_sink(); // Create a stream of messages let messages = stream::iter(vec![ LogMessage("Starting up".into()), LogMessage("Processing data".into()), LogMessage("Shutting down".into()), ]).map(Ok::<_, xtra::Error>); // Forward stream to actor via sink messages.forward(sink).await.unwrap(); // Output: // Log: Starting up // Log: Processing data // Log: Shutting down } ``` -------------------------------- ### Define Actor Behavior with `Actor` Trait in Rust Source: https://context7.com/restioson/xtra/llms.txt Defines the core behavior and lifecycle of an actor using the `Actor` trait. Includes `started` and `stopped` methods for managing actor initialization and cleanup. Supports a `Stop` type for returning values upon actor termination. The `derive` macro offers a simpler way to define actors. ```rust use xtra::prelude::*; struct Counter { count: u32, } impl Actor for Counter { type Stop = u32; // Return final count when stopped async fn started(&mut self, mailbox: &Mailbox) -> Result<(), Self::Stop> { println!("Counter actor started!"); Ok(()) } async fn stopped(self) -> Self::Stop { println!("Counter actor stopped with count: {}", self.count); self.count // Return final count } } // Using the derive macro for simpler actors with Stop = () #[derive(Default, xtra::Actor)] struct SimpleActor; #[tokio::main] async fn main() { let addr = xtra::spawn_tokio(Counter { count: 0 }, Mailbox::unbounded()); // Actor is now running } ``` -------------------------------- ### Configure xtra with Tokio and Macros in Cargo.toml Source: https://github.com/restioson/xtra/blob/master/README.md This TOML snippet shows how to configure the xtra crate in your Cargo.toml file to enable the 'tokio' and 'macros' features. These features are necessary for using xtra with the Tokio runtime and for utilizing the Actor derive macro. ```toml [dependencies] xtra = { version = "0.6.0", features = ["tokio", "macros"] } ``` -------------------------------- ### Manage Actor Communication with Address Source: https://context7.com/restioson/xtra/llms.txt Demonstrates how to use Address and WeakAddress to send messages to actors. It covers checking connection status, handling responses, and managing actor lifecycles via address references. ```rust use xtra::prelude::*; use xtra::{Address, WeakAddress}; #[derive(xtra::Actor)] struct Greeter { greeting: String, } struct Greet(String); impl Handler for Greeter { type Return = String; async fn handle(&mut self, Greet(name): Greet, _ctx: &mut Context) -> String { format!("{}, {}!", self.greeting, name) } } #[tokio::main] async fn main() { let addr: Address = xtra::spawn_tokio( Greeter { greeting: "Hello".into() }, Mailbox::unbounded() ); println!("Is connected: {}", addr.is_connected()); let response = addr.send(Greet("World".into())).await.unwrap(); println!("{}", response); let weak_addr: WeakAddress = addr.downgrade(); let addr2 = addr.clone(); println!("Same actor: {}", addr.same_actor(&addr2)); if let Some(strong) = weak_addr.try_upgrade() { let response = strong.send(Greet("Again".into())).await.unwrap(); println!("{}", response); } } ``` -------------------------------- ### Configure Message Priority and Detachment with SendFuture Source: https://context7.com/restioson/xtra/llms.txt Explains how to use SendFuture to control message execution order via priority and decouple message dispatching from result retrieval using detach. ```rust use xtra::prelude::*; #[derive(xtra::Actor)] struct Processor; struct LowPriorityTask; struct HighPriorityTask; impl Handler for Processor { type Return = &'static str; async fn handle(&mut self, _: LowPriorityTask, _ctx: &mut Context) -> &'static str { "Low priority done" } } impl Handler for Processor { type Return = &'static str; async fn handle(&mut self, _: HighPriorityTask, _ctx: &mut Context) -> &'static str { "High priority done" } } #[tokio::main] async fn main() { let addr = xtra::spawn_tokio(Processor, Mailbox::bounded(10)); let _ = addr.send(LowPriorityTask).priority(1).await; let _ = addr.send(HighPriorityTask).priority(100).await; let receiver = addr.send(LowPriorityTask).detach().await.unwrap(); println!("Message queued, doing other work..."); let result = receiver.await.unwrap(); println!("Result: {}", result); } ``` -------------------------------- ### Broadcast messages to multiple actors in Rust Source: https://context7.com/restioson/xtra/llms.txt Shows how to use addr.broadcast() to send a single message to multiple actors sharing the same mailbox. Requires messages to implement Clone, Send, and Sync, with a return type of (). ```rust use xtra::prelude::*; use std::time::Duration; #[derive(xtra::Actor)] struct Worker { id: u32 } #[derive(Clone)] struct Shutdown; #[derive(Clone)] struct Notify(String); impl Handler for Worker { type Return = (); async fn handle(&mut self, _: Shutdown, ctx: &mut Context) { println!("Worker {} received shutdown", self.id); ctx.stop_self(); } } impl Handler for Worker { type Return = (); async fn handle(&mut self, Notify(msg): Notify, _ctx: &mut Context) { println!("Worker {} notified: {}", self.id, msg); } } #[tokio::main] async fn main() { let (addr, mailbox) = Mailbox::bounded(32); tokio::spawn(xtra::run(mailbox.clone(), Worker { id: 1 })); tokio::spawn(xtra::run(mailbox.clone(), Worker { id: 2 })); tokio::spawn(xtra::run(mailbox, Worker { id: 3 })); tokio::time::sleep(Duration::from_millis(10)).await; addr.broadcast(Notify("Important update".into())).await.ok(); tokio::time::sleep(Duration::from_millis(10)).await; addr.broadcast(Shutdown).await.ok(); } ``` -------------------------------- ### Control Actor Lifecycle via Context Source: https://context7.com/restioson/xtra/llms.txt Shows how to use the Actor Context to manage internal state, access mailbox information, and trigger actor shutdown using stop_self or stop_all. ```rust use xtra::prelude::*; #[derive(xtra::Actor)] struct ControlledActor { id: u32, } struct StopSelf; struct StopAll; struct GetMailboxInfo; impl Handler for ControlledActor { type Return = (); async fn handle(&mut self, _: StopSelf, ctx: &mut Context) { println!("Actor {} stopping itself", self.id); ctx.stop_self(); } } impl Handler for ControlledActor { type Return = (); async fn handle(&mut self, _: StopAll, ctx: &mut Context) { println!("Actor {} stopping all actors on this address", self.id); ctx.stop_all(); } } impl Handler for ControlledActor { type Return = String; async fn handle(&mut self, _: GetMailboxInfo, ctx: &mut Context) -> String { let mailbox = ctx.mailbox(); let _weak_addr = mailbox.address(); format!("Actor {} mailbox accessed", self.id) } } #[tokio::main] async fn main() { let addr = xtra::spawn_tokio(ControlledActor { id: 1 }, Mailbox::unbounded()); let info = addr.send(GetMailboxInfo).await.unwrap(); println!("{}", info); addr.send(StopSelf).await.ok(); addr.join().await; println!("Actor has stopped"); } ``` -------------------------------- ### Manage actor lifecycle with xtra::run in Rust Source: https://context7.com/restioson/xtra/llms.txt Demonstrates using xtra::run to execute an actor's event loop manually. This approach allows capturing the actor's stop value upon termination. ```rust use xtra::prelude::*; #[derive(xtra::Actor)] struct CountingActor { count: u32 } struct Increment; struct Stop; impl Handler for CountingActor { type Return = u32; async fn handle(&mut self, _: Increment, _ctx: &mut Context) -> u32 { self.count += 1; self.count } } impl Handler for CountingActor { type Return = (); async fn handle(&mut self, _: Stop, ctx: &mut Context) { ctx.stop_self(); } } impl Actor for CountingActor { type Stop = u32; async fn stopped(self) -> Self::Stop { println!("Final count: {}", self.count); self.count } } #[tokio::main] async fn main() { let (addr, mailbox) = Mailbox::unbounded(); let handle = tokio::spawn(xtra::run(mailbox, CountingActor { count: 0 })); addr.send(Increment).await.ok(); addr.send(Increment).await.ok(); addr.send(Increment).await.ok(); addr.send(Stop).await.ok(); let final_count = handle.await.unwrap(); println!("Actor returned: {}", final_count); } ``` -------------------------------- ### Route messages with MessageChannel in Rust Source: https://context7.com/restioson/xtra/llms.txt Demonstrates how to use MessageChannel to send a specific message type to multiple different actor types, effectively erasing the actor type for polymorphic dispatch. ```rust use xtra::prelude::*; use xtra::message_channel::MessageChannel; struct Ping; #[derive(xtra::Actor)] struct ServerA; #[derive(xtra::Actor)] struct ServerB; impl Handler for ServerA { type Return = &'static str; async fn handle(&mut self, _: Ping, _ctx: &mut Context) -> &'static str { "Pong from Server A" } } impl Handler for ServerB { type Return = &'static str; async fn handle(&mut self, _: Ping, _ctx: &mut Context) -> &'static str { "Pong from Server B" } } #[tokio::main] async fn main() { let server_a = xtra::spawn_tokio(ServerA, Mailbox::unbounded()); let server_b = xtra::spawn_tokio(ServerB, Mailbox::unbounded()); let channels: Vec> = vec![ MessageChannel::new(server_a), MessageChannel::new(server_b), ]; for (i, channel) in channels.iter().enumerate() { let response = channel.send(Ping).await.unwrap(); println!("Server {}: {}", i + 1, response); } println!("Is connected: {}", channels[0].is_connected()); println!("Same actor: {}", channels[0].same_actor(&channels[1])); } ``` -------------------------------- ### Create Actor Mailboxes with `Mailbox` in Rust Source: https://context7.com/restioson/xtra/llms.txt Manages the message queue for an actor using the `Mailbox` type. Supports both unbounded mailboxes for unlimited capacity and bounded mailboxes for backpressure control. Creating a mailbox returns an `Address` and the `Mailbox` itself, which are used to spawn and interact with actors. ```rust use xtra::prelude::*; #[derive(xtra::Actor)] struct Worker; struct Task(String); impl Handler for Worker { type Return = String; async fn handle(&mut self, Task(name): Task, _ctx: &mut Context) -> String { format!("Completed: {}", name) } } #[tokio::main] async fn main() { // Unbounded mailbox - no backpressure, unlimited message queue let (addr_unbounded, mailbox_unbounded) = Mailbox::::unbounded(); let addr1 = xtra::spawn_tokio(Worker, (addr_unbounded, mailbox_unbounded)); // Bounded mailbox - provides backpressure when full let (addr_bounded, mailbox_bounded) = Mailbox::::bounded(10); let addr2 = xtra::spawn_tokio(Worker, (addr_bounded, mailbox_bounded)); // Check mailbox status println!("Mailbox capacity: {:?}", addr2.capacity()); // Output: Some(10) println!("Messages in queue: {}", addr2.len()); // Output: 0 println!("Is empty: {}", addr2.is_empty()); // Output: true // Send messages let result = addr1.send(Task("job1".into())).await.unwrap(); println!("{}", result); // Output: Completed: job1 } ``` -------------------------------- ### Handle Actor Communication Errors in Rust Source: https://context7.com/restioson/xtra/llms.txt Demonstrates how to define an actor, handle messages, and manage errors when sending messages to an actor that has been stopped. It utilizes the xtra::Error enum to differentiate between Disconnected and Interrupted states. ```rust use xtra::prelude::*; use xtra::Error; #[derive(xtra::Actor)] struct FragileActor; struct Work; struct Shutdown; impl Handler for FragileActor { type Return = String; async fn handle(&mut self, _: Work, _ctx: &mut Context) -> String { "work done".to_string() } } impl Handler for FragileActor { type Return = (); async fn handle(&mut self, _: Shutdown, ctx: &mut Context) { ctx.stop_self(); } } #[tokio::main] async fn main() { let addr = xtra::spawn_tokio(FragileActor, Mailbox::unbounded()); // Successful send match addr.send(Work).await { Ok(result) => println!("Success: {}", result), Err(Error::Disconnected) => println!("Actor is disconnected"), Err(Error::Interrupted) => println!("Request was interrupted"), } // Stop the actor addr.send(Shutdown).await.ok(); addr.join().await; // Wait for actor to stop // Now sends will fail match addr.send(Work).await { Ok(_) => println!("Unexpected success"), Err(Error::Disconnected) => println!("Actor is disconnected"), // This prints Err(Error::Interrupted) => println!("Request was interrupted"), } } ``` -------------------------------- ### Handle Messages with `Handler` Trait in Rust Source: https://context7.com/restioson/xtra/llms.txt Implements message handling for actors using the `Handler` trait. Each handler specifies a message type and its corresponding `Return` type, defining an asynchronous `handle` method to process incoming messages. This allows actors to manage their state and respond to different commands. ```rust use xtra::prelude::*; #[derive(xtra::Actor)] struct Calculator { result: i32, } // Define message types struct Add(i32); struct Multiply(i32); struct GetResult; impl Handler for Calculator { type Return = i32; async fn handle(&mut self, Add(value): Add, _ctx: &mut Context) -> i32 { self.result += value; self.result } } impl Handler for Calculator { type Return = i32; async fn handle(&mut self, Multiply(value): Multiply, _ctx: &mut Context) -> i32 { self.result *= value; self.result } } impl Handler for Calculator { type Return = i32; async fn handle(&mut self, _: GetResult, _ctx: &mut Context) -> i32 { self.result } } #[tokio::main] async fn main() { let addr = xtra::spawn_tokio(Calculator { result: 0 }, Mailbox::unbounded()); // Send messages and await results let result = addr.send(Add(5)).await.unwrap(); println!("After Add(5): {}", result); // Output: 5 let result = addr.send(Multiply(3)).await.unwrap(); println!("After Multiply(3): {}", result); // Output: 15 let final_result = addr.send(GetResult).await.unwrap(); println!("Final result: {}", final_result); // Output: 15 } ``` -------------------------------- ### xtra::join: Handle Messages While Awaiting a Future in Rust Source: https://context7.com/restioson/xtra/llms.txt The `xtra::join` function polls a future while allowing the actor to continue handling incoming messages. The future is guaranteed to run to completion, even if the actor is stopped. This is useful for long-running asynchronous tasks where responsiveness to other messages is crucial. ```rust use xtra::prelude::*; use std::time::Duration; #[derive(xtra::Actor)] struct AsyncWorker; struct DoWork; struct Ping; impl Handler for AsyncWorker { type Return = &'static str; async fn handle(&mut self, _: Ping, _ctx: &mut Context) -> &'static str { "pong" } } impl Handler for AsyncWorker { type Return = String; async fn handle(&mut self, _: DoWork, ctx: &mut Context) -> String { // Simulate long async operation while still handling other messages let result = xtra::join( ctx.mailbox(), self, async { tokio::time::sleep(Duration::from_millis(100)).await; "work completed" } ).await; result.to_string() } } #[tokio::main] async fn main() { let addr = xtra::spawn_tokio(AsyncWorker, Mailbox::unbounded()); // Start long work let work_handle = tokio::spawn({ let addr = addr.clone(); async move { addr.send(DoWork).await.unwrap() } }); // Actor can still handle other messages during DoWork tokio::time::sleep(Duration::from_millis(10)).await; let pong = addr.send(Ping).await.unwrap(); println!("Got: {}", pong); // Output: Got: pong let result = work_handle.await.unwrap(); println!("Work result: {}", result); // Output: Work result: work completed } ``` -------------------------------- ### Scoped Tasks - Stop Task on Actor Lifecycle with xtra::scoped Source: https://context7.com/restioson/xtra/llms.txt Demonstrates how to create a task that automatically stops when its associated actor stops using `xtra::scoped`. The function returns `Some(result)` if the task completes successfully, or `None` if the actor terminates before the task finishes. This is useful for ensuring tasks are tied to the actor's lifecycle. ```rust use xtra::prelude::*; use std::time::Duration; #[derive(xtra::Actor)] struct ManagedActor; struct DoSomething; impl Handler for ManagedActor { type Return = (); async fn handle(&mut self, _: DoSomething, _ctx: &mut Context) { println!("Actor doing something"); } } #[tokio::main] async fn main() { let addr = xtra::spawn_tokio(ManagedActor, Mailbox::unbounded()); // Create a task scoped to the actor's lifetime let scoped_task = xtra::scoped(&addr, async { println!("Scoped task started"); tokio::time::sleep(Duration::from_secs(10)).await; println!("Scoped task completed"); // Won't print if actor stops first "task result" }); // Spawn the scoped task let handle = tokio::spawn(scoped_task); // Do some work with the actor addr.send(DoSomething).await.ok(); // Drop the address to stop the actor tokio::time::sleep(Duration::from_millis(50)).await; drop(addr); // Task returns None because actor stopped let result = handle.await.unwrap(); match result { Some(value) => println!("Task completed with: {}", value), None => println!("Task cancelled - actor stopped"), // This prints } } ``` -------------------------------- ### xtra::select: Handle Messages Until Future Completes or Actor Stops in Rust Source: https://context7.com/restioson/xtra/llms.txt The `xtra::select` function handles messages while polling a future, but it returns early if the actor stops. It returns `Either::Left(result)` if the future completes successfully, or `Either::Right(future)` if the actor stops before the future finishes. This is useful for operations that should be cancelled if the actor's lifecycle ends prematurely. ```rust use xtra::prelude::*; use futures_util::future::Either; use std::time::Duration; #[derive(xtra::Actor)] struct SelectiveWorker; struct StopActor; struct TryLongOperation; impl Handler for SelectiveWorker { type Return = (); async fn handle(&mut self, _: StopActor, ctx: &mut Context) { ctx.stop_self(); } } impl Handler for SelectiveWorker { type Return = Option; async fn handle(&mut self, _: TryLongOperation, ctx: &mut Context) -> Option { let long_task = async { tokio::time::sleep(Duration::from_secs(10)).await; "completed" }; // Will handle messages while waiting, but exit early if actor stops match xtra::select(ctx.mailbox(), self, Box::pin(long_task)).await { Either::Left(result) => Some(result.to_string()), Either::Right(_unfinished_future) => { println!("Actor stopped before operation completed"); None } } } } #[tokio::main] async fn main() { let addr = xtra::spawn_tokio(SelectiveWorker, Mailbox::unbounded()); // Start long operation in background let op_handle = tokio::spawn({ let addr = addr.clone(); async move { addr.send(TryLongOperation).await } }); // Stop actor after short delay tokio::time::sleep(Duration::from_millis(50)).await; addr.send(StopActor).await.ok(); // Operation returns None because actor was stopped let result = op_handle.await.unwrap(); println!("Operation result: {:?}", result); // Output: Operation result: Ok(None) } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.