Hi, I am trying to implement the observer pattern in rust for my game. It took me a while to realize that what I had in mind was actually the observer pattern, but now that I figured it out I tried to make a mock up based on refactorguru. I tried to extend this to my particular scenario, but I realized I have two problems.
- I would like my events to be enum structs, so that they can store the event relevant data in the event, which I then can pass on to the subscriber, instead of having the subscriber having to fulfill a trait in order to call the right functions on it.
- The subscribers collection should be some map (probably a hashmap) mapping some event variant to a closure that accepts the event as an input. As you can see in the example below, I want this closure to mutate a non-owned object. So I guess that will have to work with RefCells? I am not 100% clear on the details here. Any help would be much appreciated.
I have looked at the lpxxn project's implementation of the observer pattern, but I found the lack of events in their code to render the example useless to me.
This is my code:
mod publisher {
use std::collections::HashMap;
use std::hash::Hash;
pub trait Publisher<Event, Subscriber>
where
Event: Clone + Eq + Hash,
Subscriber: Fn(Event),
{
fn get_events(&self) -> &HashMap<Event, Vec<Subscriber>>;
fn get_events_mut(&mut self) -> &mut HashMap<Event, Vec<Subscriber>>;
fn subscribe(&mut self, event_type: Event, listener: Subscriber) {
self.get_events().entry(event_type.clone()).or_default();
self.get_events()
.get_mut(&event_type)
.unwrap()
.push(listener);
}
}
}
pub struct Game {
player_publisher: player::Publisher,
log: Log,
}
impl Game {
fn new() -> Self {
let player_publisher = player::Publisher::default();
let log = Log::default();
player_publisher.subscribe(player::Event::PlayerJoins, |event| {
log.log_event_subscriber(event)
});
Self {
player_publisher,
log,
}
}
}
#[derive(Default)]
pub struct Log(Vec<player::Event>);
impl Log {
fn log_event_subscriber(&mut self, event: player::Event) {
self.0.push(event);
}
}
mod player {
use std::collections::HashMap;
use crate::publisher;
type ID = usize;
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Event {
PlayerJoins { id: ID },
PlayerLeaves { id: ID },
}
type Subscriber = fn(Event);
#[derive(Default)]
pub struct Publisher {
events: HashMap<Event, Vec<Subscriber>>,
}
impl publisher::Publisher<Event, Subscriber> for Publisher {
fn get_events(&self) -> &HashMap<Event, Vec<Subscriber>> {
&self.events
}
fn get_events_mut(&mut self) -> &mut HashMap<Event, Vec<Subscriber>> {
&mut self.events
}
}
}