I'm trying to implement a variant of the observer pattern. Currently I have it like this (after the example on Refactoring Guru here):
#[derive(Debug, PartialEq, Eq, Hash, Clone)] pub enum Event { ElementACreated, ElementADeleted, ElementBCreated, ElementBDeleted, } pub type Subscriber = fn(event: Event); #[derive(Default)] pub struct Publisher { events: HashMap<Event, Vec<Subscriber>>, } impl Publisher { pub fn subscribe(&mut self, event_type: Event, listener: Subscriber) { self.events.entry(event_type.clone()).or_default(); self.events.get_mut(&event_type).unwrap().push(listener); debug!("Subscribed to event: {:?}", event_type); } pub fn unsubscribe(&mut self, event_type: Event, listener: Subscriber) { self.events.get_mut(&event_type).unwrap().retain(|&x| x != listener); debug!("Unsubscribed from event: {:?}", event_type); } pub(crate) fn notify(&self, event_type: Event) { let listeners = self.events.get(&event_type).unwrap(); for listener in listeners { listener(event_type.clone()); } debug!("Notified {} listeners about event: {:?}", listeners.len(), event_type); } } I would like to extend it in a way that would allow me to also send event-specific payloads along in the notify function.
Example:
#[derive(Debug, PartialEq, Eq, Hash, Clone)] pub enum Event { ElementACreated // <- Also sends ElementA in notify, ElementADeleted // <- Also sends ElementAId in notify, ElementBCreated // <- Also sends ElementB in notify, ElementBDeleted // <- Also sends ElementAId in notify, } pub type Subscriber = fn(event: Event, payload: <ElementA|ElementAId|ElementB|ElementBId>); // <-- This would need some kind of extension #[derive(Default)] pub struct Publisher { events: HashMap<Event, Vec<Subscriber>>, } impl Publisher { pub fn subscribe(&mut self, event_type: Event, listener: Subscriber) { self.events.entry(event_type.clone()).or_default(); self.events.get_mut(&event_type).unwrap().push(listener); debug!("Subscribed to event: {:?}", event_type); } pub fn unsubscribe(&mut self, event_type: Event, listener: Subscriber) { self.events.get_mut(&event_type).unwrap().retain(|&x| x != listener); debug!("Unsubscribed from event: {:?}", event_type); } pub(crate) fn notify(&self, event_type: Event, payload: <ElementA|ElementAId|ElementB|ElementBId>) { // <-- This as well let listeners = self.events.get(&event_type).unwrap(); for listener in listeners { listener(event_type.clone(), payload); } debug!("Notified {} listeners about event: {:?}", listeners.len(), event_type); } } I tried extenting the Event Enum like so:
#[derive(Debug, PartialEq, Eq, Hash, Clone)] pub enum Event { ElementACreated(ElementA), ElementADeleted(ElementAId), ElementBCreated(ElementB), ElementBDeleted(ElementBId), } But then the subscribers would only ever listen to one Id or one struct as the Hash in the Publisher::events HashMap would be to restricted.
Any ideas on how to glue Event Type and Payload together to create an ergonomic and slick solution would be greatly appreciated!