actify 0.8.1

An intuitive actor model with minimal boilerplate
Documentation

Actify

Actify is a pre-1.0 crate used in production. The API may still change between minor versions.

Sharing (mutable) state across async tasks in Rust usually means juggling mutexes and channels, and a lot of boilerplate like hand-writen message enums. Actify gives you a typed, async actor model built on Tokio for any struct. Just add #[actify] to an impl block and call your methods through a clonable [Handle].

Crates.io License Docs

Installation

cargo add actify

Benefits

By generating the boilerplate code for you, a few key benefits are provided:

  • Async actor model built on Tokio and channels, which can keep arbitrary owned data types.
  • Atomic access and mutation of underlying data through clonable handles.
  • Typed arguments and return values on the methods from your actor, exposed through each handle.
  • No need to manually define message structs or enums!
  • Built-in methods like get(), set(), set_if_changed(), and subscribe() even without using the macro.
  • Automatic broadcasting of state changes to subscribers, with #[skip_broadcast] and #[broadcast] controls.
  • Local synchronization through Cache, with recv, recv_newest, and non-blocking variants.
  • Rate-limited updates through Throttle with configurable Frequency.
  • Generic type parameters supported in both actor types and method arguments.
  • Extension traits for common types: Vec, Option, HashMap, HashSet.

Example

Consider the following example, in which you want to turn your custom Greeter into an actor:

use actify::{Handle, actify};

#[derive(Clone, std::fmt::Debug)]
struct Greeter {}

#[actify]
impl Greeter {
    fn say_hi(&self, name: String) -> String {
        format!("hi {}", name)
    }
}

#[tokio::main]
async fn main() {
    // An actify handle is created and initialized with the Greeter struct
    let handle = Handle::new(Greeter {});

    // The say_hi method is made available on its handle through the actify! macro
    let greeting = handle.say_hi("Alfred".to_string()).await;

    // The method is executed remotely on the initialized Greeter and returned through the handle
    assert_eq!(greeting, "hi Alfred".to_string())
}

Reactive Subscriptions

Actors broadcast state changes automatically. Subscribers and caches let you react to updates without polling:

use actify::Handle;

#[tokio::main]
async fn main() {
    let handle = Handle::new(0);

    // Subscribe to raw broadcast events
    let mut rx = handle.subscribe();

    // Or create a local cache that stays in sync
    let mut cache = handle.create_cache().await;

    handle.set(42).await;

    // The subscriber receives every change
    assert_eq!(rx.recv().await.unwrap(), 42);

    // The cache provides synchronous access to the latest value
    assert_eq!(cache.get_newest(), &42);
}

For full API documentation, see docs.rs.