Store handlers with different signatures

Hi Rustaceans!

Goal

I'm trying to find a way to allow users to plug in custom coding (closures) when certain events are processed.
One possible solution is to implement an event dispatcher. Each event type has a corresponding handler function with an own signatures (input and return type).

Example usage could look like this:

let mut dispatcher = Dispatcher::new();
dispatcher.register(|input: InputType1| {
  ReturnType1 { foo: "bar".to_string() }
});
dispatcher.register(|input: InputType2| {
  ReturnType2 { baz: "qaz".to_string() }
});
let res1 = dispatcher.run(InputType1 { x: "y".to_string()); // gives ReturnType1 { foo: "bar" }
let res2 = dispatcher.run(InputType2 { a: "b".to_string()); // gives ReturnType2 { baz: "qaz" }

Problem

I'm unsure how to store handler functions because their signatures differ. It would be great if Rust's type system can make sure that only certain input/return type pairs are allowed (InputType1+ReturnType1 and InputType2+ReturnType2 in the example).
Is there maybe a better approach to tackle this problem? The handler doesn't need to be a closure, a struct implementing a trait is also fine.

Thanks a ton and best regards,
David

It's not feasible for the interface to be register(some_closure) because the type system, in principle (though not in practice on current stable), allows a single type to have more than one Fn signature. So, if you want that implicit event-type selection, it will have to be via a custom trait (with associated types for input and output).

Then, for the storage, you can make fn register itself a method of another trait, which is implemented once for each supported event type, and each implementation knows which (appropriately typed) list of handlers to store the handler in. The type of that list will be like Vec<dyn Handler<Input = InputType1, Return = ReturnType1>>.

You could probably hack something together using poly_fn in frunk - Rust, but I wouldn't really recommend it. A better solution might be an enum of all events with their input and output types.

That's called an "associated type". Here's a solution.

4 Likes

@David-Kunz Or using HList, if you want to have to prove at compile-time that a certain type of function exists: Rust Explorer (Please don't use it, very overengineered and POC, but it was fun)

Wow, I'm absolutely amazed by the presented solutions, thanks so much for taking your time!

The solution by @H2CO3 seems to be the cleanest, so I will mark it as the solution.

Thanks again, really appreciated!
David

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.