| Crates.io | priact |
| lib.rs | priact |
| version | 0.1.1 |
| created_at | 2025-06-18 18:03:47.177071+00 |
| updated_at | 2025-06-18 18:08:58.520905+00 |
| description | A lightweight and ergonomic Actor implementation for Rust, built on tokio, featuring explicit message prioritization via a BinaryHeap. |
| homepage | |
| repository | https://github.com/drewburkhart/priact |
| max_upload_size | |
| id | 1717454 |
| size | 29,143 |
priact π¦A lightweight and ergonomic Actor implementation for Rust, built on tokio, featuring explicit message prioritization via a BinaryHeap. This is based on the Actor concept from Swift.
tokio::mpsc channels for efficient, non-blocking communication with actors.Low, Medium (the default), or High priority, allowing critical operations to be processed ahead of others.define_actor! Macro: Simplifies actor definition by automatically generating message enums and handle logic, reducing boilerplate.tokio: Seamlessly integrates with the tokio asynchronous runtime.priact?While Rust's ownership system makes data races less common, managing mutable state across asynchronous tasks can still be challenging. The Actor Model provides a clear pattern for this, and priact offers:
define_actor! macro makes defining actors straightforward and enjoyable.Add priact to your Cargo.toml:
[dependencies]
priact = "0.1.0" # Check crates.io for the latest version
tokio = { version = "1", features = ["full"] } # Or specific features you need
async-trait = "0.1" # Required by the Actor trait
Define your actor and its messages using the define_actor! macro:
use priact::{define_actor, spawn_actor, Actor, Priority};
use tokio::sync::oneshot;
// Define your actor's state and its methods
define_actor! {
/// A simple counter actor.
TestCounter {
count: i32,
}
// Define the messages your actor can handle
impl TestCounterMsg {
// Methods prefixed with @priority will be translated into messages
// `self` here refers to the actor's internal state
// High-priority βreadβ message
@priority(High)
fn GetValue(&mut self, tx: oneshot::Sender<i32>) {
let _ = tx.send(self.count);
}
// Low-priority βwriteβ message
@priority(Low)
fn Increment(&mut self, ack: oneshot::Sender<()>) {
self.count += 1;
let _ = ack.send(());
}
// Medium-priority asynchronous decrement
@priority(Medium)
async fn DecrementAsync(&mut self) {
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
self.count -= 1;
}
}
}
#[tokio::main]
async fn main() {
// Create actor state
let counter = TestCounter { count: 0 };
// Spawn it, getting back a `mpsc::Sender<TestCounterMsg>`
let tx = spawn_actor(counter);
// Send some messages...
let (ack_tx, ack_rx) = oneshot::channel();
tx.send(TestCounterMsg::Increment(ack_tx)).await.unwrap();
ack_rx.await.unwrap();
// Query the current count
let (resp_tx, resp_rx) = oneshot::channel();
tx.send(TestCounterMsg::GetValue(resp_tx)).await.unwrap();
let value = resp_rx.await.unwrap();
println!("Current count = {}", value);
// Shut down the actor
tx.send(TestCounterMsg::Shutdown).await.unwrap();
}
BinaryHeap<PrioritizedWrapper>.handle on the actor, and repeats.Explicit: A Shutdown variant returns false from handle, tearing down both tasks.
Implicit: Dropping all Sender handles drains the queue then stops.
define_actor!: Macro for defining actors and their messages.spawn_actor<A>(actor: A) -> mpsc::Sender<A::Msg>: Spawns an actor into a tokio task and returns a sender for its messages.Actor trait: Defines the behavior of an actor, requiring a Msg type and a handle method.Prioritized trait: Needs to be implemented by your message enum to specify priority.Priority enum: Low, Medium, High.For detailed API documentation, please refer to docs.rs.
Thank you to @lucretiel and @ipetkov for help with the macro!
Contributions are welcome! Feel free to open issues or submit pull requests.
This project is licensed under the MIT License.