Expand description

messages is a runtime-agnostic actor library.

It is heavily inspired by actix, a great actor framework.

This crate can be used with any runtime, whether it popular or not. However, for the biggest one (tokio and async-std) there is an optional built-in support enabling more convenient interface (such as an automatic actor spawning).

Asyncness

In order to provide convenient interface, this crate uses async_trait to declare traits with async methods. To make the experience more convenient, async_trait::async_trait macro is publicly re-exported in the prelude module.

Examples

With runtime features

use messages::prelude::*;

struct Example; // Most of the types can be an actor.

// While `Actor` implementation can be customized, it is not required.
#[async_trait]
impl Actor for Example {}

// Message handler that calculated sum of two numbers.
#[async_trait]
impl Handler<(u8, u8)> for Example {
    type Result = u16;
    async fn handle(&mut self, (a, b): (u8, u8), context: &Context<Self>) -> u16 {
        (a as u16) + (b as u16)
    }
}

// Notification handler that calculated just writes received number to stdout.
#[async_trait]
impl Notifiable<u8> for Example {
    async fn notify(&mut self, input: u8, context: &Context<Self>) {
        println!("Received number {}", input);
    }
}

#[tokio::main]
async fn main() {
   let mut addr = Example.spawn();
   let result = addr.send((22, 20)).await.unwrap();
   assert_eq!(result, 42);
   addr.notify(42).await.unwrap();
   addr.stop().await;
   addr.wait_for_stop().await;  
}

Without runtime features

use messages::prelude::*;

struct Ping;

#[async_trait]
impl Actor for Ping {}

#[async_trait]
impl Handler<u8> for Ping {
    type Result = u8;
    async fn handle(&mut self, input: u8, context: &Context<Self>) -> u8 {
        input
    }
}

#[tokio::main]
async fn main() {
   let context = Context::new();
   let mut addr = context.address();
   let actor = Ping;
   // Could've been any other runtime.
   let mut task_handle = tokio::spawn(context.run(actor));
   let result = addr.send(42).await.unwrap();
   assert_eq!(result, 42);
   addr.stop().await;
   addr.wait_for_stop().await;
   task_handle.await.unwrap();
}

Main entities

Main entites of this crate:

With runtime features enabled, there are also several more points of interest:

  • Registry: Collection of independent, unique and named actors.
  • Service: Actor that can be stored in the registry.
  • Coroutine: Alternative to the Handler trait that allows parallel message processing.

Modules

Actor is an entity capable of receiving and processing messages.

An address of an actor.

Context represents an environment in which actor is being executed.

Errors that can occur during the actor interaction workflow.

In order for an Actor to be able to process messages, it should have logic associated with them.

Collection of the main types required to work with messages crate.

registryruntime-tokio or runtime-async-std

Registry provides a way to get addresses of singleton-like addresses by automatically managing their lifetime under the hood.