Hi,
since a few hours, I try to overcome the following problem:
I have a struct with a method that registers event handlers. Every event handler handles a different event type, which can't be united as enum, but have a trait mark implemented.
The problem is, that I can't figure out a good way to store the event handlers.
Here is what I'd like to archive:
fn main() {
let br = Browser{};
// register event handlers:
br.on<EventA>(|a| println!("{}", a.foo));
br.on<EventB>(|b| println!("{}", b.bar));
// `a` is of type `EventA` and `b` is of type `EventB`
// Simplified example. Events have all very different fields and event handlers need access to all
}
And here is how I tried to archive it (minimal example). This code is not working, but I hope you can see my approach.
trait DevToolsProtocolEvent {
const IDENT: &'static str;
}
struct EventA {
foo: String
}
impl DevToolsProtocolEvent for EventA {
const IDENT: &'static str = "Domain.name1";
}
struct EventB {
bar: String
}
impl DevToolsProtocolEvent for EventB {
const IDENT: &'static str = "Domain.name2";
}
// There is a high number of events, and events can get removed or added, so an enum is probably not a good solution
struct Browser<Event, EventHandler>
where
Event: DevToolsProtocolEvent,
EventHandler: FnMut(Result<Event>),
{
event_handlers: HashMap<&'static str, Box<EventHandler>>,
// ^ This is where my problem is. How do I define `event_handlers`?
// Ideally, without type parameters that I have to copy everywhere.
// It seems if I use `Browser` anywhere, that thing also needs to implement these type parameters (for example, I have a builder struct with different `build` methods which all have to implement these type parameters, which is a major pain point, to be honest.)
// For example `pub fn start_and_connect<E, EH>(&self) -> Result<Browser<E, EH>> {`, which results in a "trait bound E is not satisfied" error (so it seems even more code is needed)
}
impl<Event, EventHandler> Browser<Event, EventHandler> where
Event: DevToolsProtocolEvent,
EventHandler: FnMut(Result<Event>),
{
fn on<E>(&mut self, callback: &mut EventHandler)
where
E: DevToolsProtocolEvent
// Is it somehow possible to reuse the `Event` type parameter?
// I tries `fn on<E: Event>`, but that is not allowed (in the full code there are more constraints, so that would be useful)
{
self.event_handlers.entry(E::IDENT).insert(Box::new(callback));
// By the way, here I'd like to use `E` itself, instead of `E::IDENT` is that somehow possible? My understanding is, that `E` is something that is not available at run time, but maybe I'm wrong and there is a way.
}
}
What would be the best way to implement event handlers like that? Is there a better way to structure my code?