Dactor
A abstract framework for distributed actor
#dactor
Write your actor logic once. Swap the runtime underneath.
dactor is a provider-agnostic actor framework for Rust. You write actors against dactor's API and choose a runtime adapter — ractor, kameo, or coerce — without changing your business logic.
#Quick Start
Add dactor to your project:
[dependencies]
dactor = { version = "0.2", features = ["test-support"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "time"] }
Build a counter actor in four steps — define, message, handle, run:
use dactor::prelude::*;
use dactor::message::Message;
use dactor::test_support::test_runtime::TestRuntime;
// 1. Define your actor
struct Counter { count: u64 }
impl Actor for Counter {
type Args = Self;
type Deps = ();
fn create(args: Self, _deps: ()) -> Self { args }
}
// 2. Define messages
struct Increment(u64);
impl Message for Increment { type Reply = (); }
struct GetCount;
impl Message for GetCount { type Reply = u64; }
// 3. Implement handlers
#[async_trait::async_trait]
impl Handler<Increment> for Counter {
async fn handle(&mut self, msg: Increment, _ctx: &mut ActorContext) {
self.count += msg.0;
}
}
#[async_trait::async_trait]
impl Handler<GetCount> for Counter {
async fn handle(&mut self, _msg: GetCount, _ctx: &mut ActorContext) -> u64 {
self.count
}
}
// 4. Spawn and interact
#[tokio::main]
async fn main() {
let runtime = TestRuntime::new();
let counter = runtime.spawn::<Counter>("counter", Counter { count: 0 })
.await.unwrap();
counter.tell(Increment(5)).unwrap(); // fire-and-forget
counter.tell(Increment(3)).unwrap();
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
let count = counter.ask(GetCount, None).unwrap().await.unwrap();
println!("Count: {count}"); // → 8
}
Run this example: cargo run --example readme_quickstart -p dactor --features test-support
#Using a Runtime Adapter
The quick start above uses TestRuntime (great for unit tests). For
production, pick a runtime adapter:
[dependencies]
dactor = "0.2"
dactor-ractor = "0.2" # or dactor-kameo / dactor-coerce
tokio = { version = "1", features = ["rt-multi-thread", "macros", "time"] }
Your actor code stays exactly the same — only the runtime setup changes.
All adapters expose the same ActorRef<A> interface.
| Adapter | Runtime | Best for |
|---|---|---|
dactor-ractor |
ractor | Erlang-style supervision, mature ecosystem |
dactor-kameo |
kameo | Lightweight, Tokio-native |
dactor-coerce |
coerce | Built-in clustering support |
#What You Can Do
dactor supports five communication patterns:
| Pattern | Code | When to use |
|---|---|---|
| Tell | actor.tell(msg) |
Fire-and-forget commands |
| Ask | actor.ask(msg, cancel) |
Request-reply queries |
| Expand | actor.expand(msg, ...) |
One request → stream of responses |
| Reduce | actor.reduce(stream, ...) |
Stream of inputs → one response |
| Transform | actor.transform(stream, ...) |
Stream in → stream out |
Plus broadcast messaging, actor pools, interceptors, supervision, persistence, bounded mailboxes, metrics, and remote actor support.
#Learn More
📖 Usage Guide — step-by-step tutorial covering all features, from your first actor to distributed clusters.
🏃 Examples — 17 runnable examples:
| Example | What it teaches | Run it |
|---|---|---|
basic_counter |
Tell + ask basics | cargo run --example basic_counter -p dactor --features test-support |
streaming |
Expand + reduce patterns | cargo run --example streaming -p dactor --features test-support |
supervision |
Supervision strategies | cargo run --example supervision -p dactor --features test-support |
interceptors |
Logging, auth, rate limiting | cargo run --example interceptors -p dactor --features test-support |
actor_pool |
Pool routing strategies | cargo run --example actor_pool -p dactor --features test-support |
persistence |
Event sourcing + snapshots | cargo run --example persistence -p dactor --features test-support |
bounded_mailbox |
Backpressure with overflow | cargo run --example bounded_mailbox -p dactor --features test-support |
task_queue |
Worker pool + retries | cargo run --example task_queue -p dactor --features test-support,metrics |
showcase |
All features combined | cargo run --example showcase -p dactor --features test-support |
All 17 examples
| Example | Description |
|---|---|
readme_quickstart |
Minimal counter (this README) |
basic_counter |
Tell + ask patterns |
streaming |
Server + client streaming |
batch_streaming |
Transparent batching |
cancellation |
Cancellation tokens |
supervision |
OneForOne, AllForOne, RestForOne |
interceptors |
Inbound/outbound pipelines |
dead_letters |
DropObserver monitoring |
persistence |
Journal + snapshots |
bounded_mailbox |
Overflow strategies |
metrics |
MetricsInterceptor |
rate_limiting |
Outbound throttling |
error_handling |
ErrorCode + error chains |
task_queue |
Distributed task queue |
event_sourcing |
CQRS patterns |
actor_pool |
Pool routing |
showcase |
Comprehensive demo |
#Building & Testing
Protocol Buffer compilation uses a vendored protoc; no system installation is required.
# Run all tests cargo test --workspace --exclude dactor-test-harness --features test-support # Run E2E tests (requires building test-node first) cargo build -p dactor-ractor --features test-harness --bin test-node-ractor cargo test -p dactor-ractor --test e2e_tests --features test-harness
#Architecture
┌──────────────────────────────────────────────────┐ │ Your Application Code │ ├──────────────────────────────────────────────────┤ │ dactor (core traits + API) │ ├──────────┬──────────┬──────────┬─────────────────┤ │ dactor │ dactor │ dactor │ dactor-mock │ │ -ractor │ -kameo │ -coerce │ (testing) │ └──────────┴──────────┴──────────┴─────────────────┘
#Documentation
- 📖 Usage Guide — comprehensive tutorial
- 📐 Design Document — API design and rationale
- 📋 API Reference — generated Rust docs
#License
MIT — see LICENSE for details.