Expected a closure that implements the `Fn` trait, but this closure only implements `FnMut`

I'm not familiar with the crates involved, but looked at the API docs a bit. It looks like warp::Filter::map expects a closure that is going to be executed in a multi-threaded environment, hence the need for Fn instead of FnMut.

For multi-threaded use, redis seems to offer MultiplexedConnection, or ConnectionManager. (I don't actually know how to properly construct the former.)

You will most likely need to add move to your closure anyways in order to support a 'static bound for warp::serve. I.e. .map(move || { ... }). Inside of the closure, you make sure it stays a Fn by cloning the captured MultiplexedConnection/ConnectionManager and then working with that clone. If you want to do a query in the .map, it might make sense to use .then instead of .map, and AsyncCommands in order to write async code.


To give some more details to how the above relates to Fn/FnMut:

A closure that implements Fn is a closure that can be called with shared (&self) access to the closure, a closure that only implements FnMut but not Fn can only be called with exclusive mutable (&mut self) access to the closure. This means that via FnMut the closure cannot be called multiple times in parallel, e.g. on multiple threads, because &mut _ references are always exclusive; but in turn, FnMut allows for mutable access to captured variables.

Your closure calls a &mut self method on a captured variable, that is: it calls rediscon.set(...), which means it can implement FnMut, but Fn is no longer possible.

If, as I described above, you were to use e.g. a ConnectionsManager, and called let my_manager = manager.clone();, then that manager.clone() call would be a &self method on the captured managers variable; further down in the closure, &mut-methods on the variable my_manager would still be allowed as that would be a variable that's local to the closure.


Regarding the concrete solution of using MultiplexedConnection/ConnectionManager, feel free to alternatively consider following different advice from anyone more knowledgeable than me about what the best practical solution here would be; as mentioned, I've just briefly looked at the API, so I cannot answer and questions about what the best trade-off would be.

2 Likes