Hi all,
I'm sure the experienced people in this group will find my question trivial and easy to answer. Still, despite a lot of reading, I'm not able to grasp what am I doing wrong regarding trait objects.
Here my problem:
I'm working on an message-passing system (event-based if you want). I've tried to define a base trait for the definition of the messages, so that I can use the trait objects later in the definition of structs methods etc. Here the code snippet:
pub trait IEvent: ToJsonMessage + FromJsonMessage{
// The get_message_type_name function is used to get the message type name.
// It returns a string slice.
fn get_message_type_name(&self) -> &'static str;
}
/// The ToJsonMessage trait is used to serialize a struct into a json string.
pub trait ToJsonMessage{
// The to_json function is used to serialize a struct into a json string.
// It returns a string.
// # Arguments
// * `self` - A reference to the struct.
fn to_json(&self) -> String
where Self: Serialize {
serde_json::to_string(&self).unwrap()
}
}
/// The FromJsonMessage trait is used to deserialize a json string into a struct.
pub trait FromJsonMessage{
// The from_json function is used to deserialize a json string into a struct.
// It takes a json string as input and returns a struct.
// # Arguments
// * `json` - A string slice.
fn from_json<T>(json: &str) -> T
where T: for<'a> Deserialize<'a>{
let object: Result<T, serde_json::Error> = serde_json::from_str(json);
object.unwrap()
}
}
After receiving a message via a messaging-infrastructure, I have to deliver the message to all the Actors (i'm trying to adopt an actor model) that subscribed to the message itself. An Actor can subscribe to more than one message and here is the source of the problem: I need to find a way of, when receiving the message in the Actor (which is a struct) call the correct struct method to handle the message. This method receives as parameter the deserialized version of the json message (in the shape of an implementation of 'IEvent'). so I need a mean to, once I receive the message via a tokio::channel, dispatch it to the correct struct method. I want to avoid having extense if-else statements so I tried the following (and obviously failed):
// The WorkOrderManager struct represents a manager for work orders.
pub struct WorkOrderManager{
... // Other irrelevant data here
list_of_subscriptions: HashMap<String, fn(Arc<dyn IEvent>)> // Here I get the compilation error
}
impl WorkOrderManager {
// The new function is used to create a new WorkOrderManager.
// It returns a WorkOrderManager.
pub fn new() -> WorkOrderManager {
let (tx, rx) = tokio::sync::mpsc::channel(100);
WorkOrderManager {
...// Other irrelevant stuff here.
list_of_subscriptions: HashMap::new()
}
}
fn register_local_subscriptions(&mut self){
let closure = |message: Arc<WorkOrderCreated>| { self.handle_work_order_created(message)};
self.list_of_subscriptions.insert(String::from(WorkOrderCreated::get_message_type_name()), closure);
}
fn handle_work_order_created(&self, event: Arc<WorkOrderCreated>) {
println!("Work Order created received");
}
}
#[async_trait]
impl Actor for WorkOrderManager {
// The as_any method is used to get a reference to the actor as a dyn Any.
fn as_any_mut(&mut self) -> &mut dyn Any{
self
}
// The get_address method is used to get the address of the actor.
fn get_address(&self) -> Arc<ActorAddress>{
self.actor_address.clone()
}
// The get_list_of_subscriptions method is used to get a list of message types that the actor is subscribed to.
fn get_list_of_subscriptions(&self) -> Vec<&String>{
self.list_of_subscriptions.keys().collect()
}
// The get_behavior_change_notifier method is used to get the behavior change notifier of the actor.
// The notifier is used to notify interested parties when an actor changes its behavior.
fn get_behavior_change_notifier(&self) -> Arc<Notify>{
self.notifier.clone()
}
// The get_id method is used to get the unique identifier of the actor.
fn get_id(&self) -> ActorId{
self.actor_id.clone()
}
// The activate method is used to activate the actor.
async fn activate(&mut self){
let receiver = self.receiver.clone();
tokio::task::spawn(async move {
let mut receiver = receiver.lock().await;
loop {
let envelope = receiver.recv().await.unwrap();
match envelope.as_ref() {
Envelope::Event(event) => {
// Handle event
let message_type = event.get_message_type();
let subscription = self.list_of_subscriptions.get(message_type).unwrap();
subscription(event.clone());
},
Envelope::Command(command) => {
// Handle command
todo!()
}
}
}
});
}
}
Here an example of the implementation of IEvent:
#[derive(Serialize, Deserialize)]
pub struct WorkOrderAborted{
pub base: EventBase,
work_order_id: String,
assay: String
}
impl WorkOrderAborted{
pub fn new(origin: &str, version: &str, name: &str, work_order_id: String, assay: String) -> Self{
Self { base: EventBase::new(origin, version, name, "payload here"), work_order_id, assay }
}
}
impl ToJsonMessage for WorkOrderAborted{}
impl FromJsonMessage for WorkOrderAborted{}
impl IEvent for WorkOrderAborted{
fn get_message_type_name(&self) -> &'static str {
"WorkOrderAborted"
}
}
I'm getting the IEvent cannot be made into an object
and I'm not really sure why is it. I would really appreciate some enlightment here.
Thanks a lot in advance.