I have a struct that defines multiple async
methods, and I'd like to store a pointer of each of them in a HashMap
, so that I can call any method in one single line, knowing only a key that is given in parameter.
The aim here is to avoid as much as possible to have a huge match
clause that would inflate more and more as I add new methods to my struct.
Methods all have the same signature:
async fn handle_xxx(&self, c: Command) -> Result<String, ()>
And I'd really like to call them the following way:
pub async fn execute_command(&mut self, command: Command) -> Result<String, ()> {
let handler = self.command_callbacks.get(&command);
let return_message: String = match handler {
Some(f) => f(self, command).await.unwrap(), // The call is made here.
None => return Err(()),
};
Ok(return_message)
}
However, obviously, in order to store something in a HashMap
, you have to specify its type when declaring the HashMap
, and that's when the trouble starts.
I tried the most obvious, which is declaring the wrapping function type:
type CommandExecutionNotWorking = fn(&CommandExecutor, Command) -> Future<Output = Result<String, ()>>;
Which does not work since Future is a trait, and not a type.
I tried to declare a generic type and specify it somewhere below:
type CommandExecutionNotWorkingEither<Fut> = fn(&CommandExecutor, Command) -> Fut;
But I encounter the same kind of issue since I need to specify the Future
type, and have a HashMap
declaration like following:
let mut command_callbacks: HashMap<
Command,
CommandExecutionFn<dyn Future<Output = Result<String, ()>>>,
> = HashMap::new();
impl Future
obviously does not work since we're not in a function signature, Future
either since it's not a type, and dyn Future
creates a legitimate type mismatch.
Thus I tried to use Pin
so that I can manipulate dyn Future
s, and I ended up with the following signature:
type CommandExecutionStillNotWorking = fn(
&CommandExecutor,
Command,
) -> Pin<Box<dyn Future<Output = Result<String, ()>>>>;
But I need to manipulate functions that return Pin<Box<dyn Future<...>>>
and not just Future
s. So I tried to define a lambda that take an async
function in parameter and returns a function that wraps the return value of my async
method in a Pin<Box<...>>
:
let wrap = |f| {
|executor, command| Box::pin(f(&executor, command))
};
But the compiler is not happy since it expects me to define the type of f
, which is what I tried to avoid here, so I'm back to square one.
Thus my question: Do you know if it's actually possible to write the type of an async
function so that pointers on them can be easily manipulated like any variable or any other function pointer?
Or should I go for another solution that may be less elegant, with a bit of duplication code or a huge match
structure?