For quite some time, I've had the goal of achieving an API like this:
struct GetEvent;
fn handler(_event: &mut GetEvent) {}
pub fn main() {
let mut server = Server::default();
server.handle(handler);
server.emit(PostEvent);
}
The names and types are just examples, this actual program has nothing to do with HTTP
The last two days, I've returned to the problem once again. And I felt so close a couple of times, but I just can't quite get it. I'm really pulling my hair out.
Below you will find the things I've tried, you can skip all of this, if you immediately know what to do and are wiser than me :D
First, the basic structure is available here. This one compiles, so the relevant methods have been stubbed with todo!()
.
So, here we go:
Naively, I just looked up on how to use any, and created this hash map:
struct Server {
handlers: HashMap<TypeId, Box<dyn Handler<dyn Any>>>,
}
fn handle<E>(&mut self, handler: impl Handler<E>) {
self.handlers.insert(TypeId::of::<E>(), Box::new(handler));
}
However:
--> src/main.rs:26:49
|
26 | self.handlers.insert(TypeId::of::<E>(), Box::new(handler));
| ^^^^^^^^^^^^^^^^^ the trait `for<'a> Fn(&'a mut (dyn Any + 'static))` is not implemented for `impl Handler<E>`, which is required by `impl Handler<E>: Handler<(dyn Any + 'static)>`
|
I honestly don't have any idea what this means. But I'm assuming, this just doesn't work.
Playground
struct Server {
handlers: HashMap<TypeId, Box<dyn Any>>,
}
fn handle<E: 'static>(&mut self, handler: impl Handler<E>) {
self.handlers.insert(TypeId::of::<E>(), Box::new(handler));
}
And this... compiles! Victory! Well sadly not. After reading about down casting for a good hour, I got a runtime error!
fn emit<E: 'static>(&mut self, mut event: E) {
let Some(handler) = self.handlers.get_mut(&TypeId::of::<E>()) else {
println!("did not find handler...");
return;
};
let handler = handler.downcast_ref::<Box<dyn Handler<E>>>().unwrap();
handler.handle(&mut (event as E));
}
thread 'main' panicked at src/main.rs:36:69:
called `Option::unwrap()` on a `None` value
But I think I understand why this isn't working. The registration function, fn handle<E: 'static>(...)
, will be turned into static dispatch, so the type won't be Box<dyn Handler<E>>
, but: alloc::boxed::Box<rust_test::handler>
. Which is the static function pointer of my handler.
Playground
Handler
trait first?fn handle<E: 'static>(&mut self, handler: impl Handler<E>) {
let handler = Box::new(handler) as Box<dyn Handler<E>>;
self.handlers.insert(TypeId::of::<E>(), handler);
}
No! Because... err, no!
= note: expected struct `Box<(dyn Any + 'static)>`
found struct `Box<dyn Handler<E>>`
Aha! Require Any + 'static
as base trait for handler? Apparently not:
cannot cast `dyn Handler<E>` to `dyn Any`, trait upcasting coercion is experimental
Playground