Data

The essential role of actors is to pass data through channels from one client to another client by the mean of their respective actorRead/Write interfaces . The data is encapsulated into a tuple structure Data<U>:

pub struct Data<U: UniqueIdentifier>(<U as UniqueIdentifier>::DataType, PhantomData<U>);

Each container Data<U> is uniquely defined with a type parameter U, the trait bound on U means that U must implement the UniqueIdentifier trait and the actual type of the data that is moved around is given by the trait associated type UniqueIdentifier::DataType.

As an example, lets define 2 clients ClientA and ClientB and a double precision vector Vec<f64> that must be transferred from ClientA to ClientB. To do so, one needs

  • first to define U:
pub enum A2B {}

here U is an empty enum. U can be of any type however empty enums are very efficient in terms of zero-cost abstraction as they entirely vanished after compilation.

  • then to implement the trait UniqueIdentifier:
impl UniqueIdentifer for A2B {
  type DataType = Vec<f64>;
}

This is where the actual type of the data to be transferred, is defined.

Note that there is a derive macro UID that implements the UniqueIdentifier trait on any type that the derive attribute is applied to, so we could have written instead:

#[derive(UID)]
#[uid(data="Vec<f64>")]
pub enum A2B {}

The derive macro uses Vec<f64> as the default type for DataType, so an even simpler declaration is

#[derive(UID)]
pub enum A2B {}

After that the Read and Write traits are implemented:

  • Write
impl Write<A2B> for ClientA {
   fn write(&mut self) -> Option<Data<A2B>> { ... }
}
  • Read
impl Read<A2B> for ClientB {
   fn read(&mut self, data: Data<A2B>) { ... }
}

One may choose as well, to implement the trait Size<U: UniqueIdentifer> for some of the clients. The trait provides the definition of the interface to get the size of the data that is written out:

impl Size<A2B> for ClientA {
   fn len(&self) -> usize {
       get_size_from_client(&self)
   }
}

If needs be, an existing type data identifier U can be replicated as long as the duplicate applies to the same client. As an example let define A2BDPLGR, the doppelganger of A2B:

#[derive(UID)]
#[alias(name = "A2B", client = "ClientA", traits = "Write,Size")]
pub enum A2BDPLGR {}

The derive attribute macro in that case will also implements, in addition to the trait UniqueIdentifier, the traits Write<A2BDPLGR> and Size<A2BDPLGR> for ClientA, each one being a wrapper for the calls to the implementation of the traits Write<A2B> and Size<A2B>, respectively.