acteur 0.9.1

A safe actor system that just works. Simple, robust, fast, documented.
Documentation

Main Features

Acteur uses async_std under the hood. You can find the all the information in the documentation.

This actor system work under the following premises:

  • Simple: The API should be small, simple and intuitive. No surprises.
  • Fast: The system should be fast and use all available CPU cores.
  • Documented: Everything must be documented with exhaustive examples.

Regarding the implementation:

  • Acteur is asynchronous and uses async_std under the hood. (Even for mutexes)
  • Actors have an ID which type is defined by the developer.
  • Messages are routed to an Actor and an *ID.
  • Actor life-cycle is automatically managed by the framework.
  • Messages for the same Actor & ID are sequential. Everything else is executed concurrently.
  • Services are provided for other concurrency forms.
  • Services don't have ID and are concurrent.
  • Services can subscribe to messages and everyone can publish messages.
  • Acteur is global, only one instance can exist.

State of the implementation

The overall feature set is complete. Acteur will continue improving and adding improvements. As for now, this framework is actively supported/developed.

My main focus of work now is in the ergonomics.

  • ☑️ Actor / services is activated on first message
  • ☑️ Actor can send messages to other actors / services
  • ☑️ System can send messages to any actor / service
  • ☑️ Actors / Services can optimally, respond to messages
  • ☑️ Services (statefull or stateless, like actors, without ID and concurrent)
  • ☑️ Automatic deallocation of unused actors (after 5 minutes without messages)
  • ☑️ Services can subscribe to messages

Examples

Simple example

use acteur::{Actor, Receive, Assistant, Acteur};
use async_trait::async_trait;

#[derive(Debug)]
struct Employee {
    salary: u32
}

#[async_trait]
impl Actor for Employee {
    type Id = u32;
    async fn activate(_: Self::Id, _: &Assistant<Self>) -> Self {
        Employee {
            salary: 0 // Load from DB or set a default,
        }
    }
}

#[derive(Debug)]
struct SalaryChanged(u32);

#[async_trait]
impl Receive<SalaryChanged> for Employee {
    async fn handle(&mut self, message: SalaryChanged, _: &Assistant<Employee>) {
        self.salary = message.0;
    }
}

fn main() {
  let sys = Acteur::new();

  sys.send_to_actor_sync::<Employee, _>(42, SalaryChanged(55000));

  sys.wait_until_stopped();
}

You can find more examples in the examples folder.

Safe Rust

No unsafe code was directly used in this crate. You can check in lib.rs the #![deny(unsafe_code)] line.

License