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.