I think Rust is also able to hide certain things.
Without async things are fine:
type Handler = fn(Request) -> Result, Error>;
let mut map: HashMap = HashMap::new();
map.insert("/", |req| { Ok(Response::new("hello".into())) });
map.insert("/about", |req| { Ok(Response::new("about".into())) });
Sure, using function pointer `fn` instead of one of the Fn traits is a bit of a cheating, but realistically you wouldn't want a handler to be a capturing closure anyway.
But of course you want to use async and hyper and tokio and your favorite async db connection pool. And the moment you add `async` to the Handler type definition - well, welcome to what author was describing in the original blog post. You'll end up with something like this
type Handler = Box BoxFuture + Send + Sync>;
type BoxFuture = Pin + Send>>;
plus type params with trait bounds infecting every method you want pass your handler to, think get, post, put, patch, etc.
pub fn add(&mut self, path: &str, handler: H)
where
H: Fn(Request) -> F + Send + Sync + 'static,
F: Future + Send + 'static,
And for what reason? I mean, look at the definitions
fn(Request) -> Result, Error>;
async fn(Request) -> Result, Error>;
It would be reasonable to suggest that if the first one is flexible enough to be stored in a container without any fuss, then the second one should as well. As a user of the language, especially in the beginning, I do not want to know of and be penalized by all the crazy transformations that the compiler is doing behind the scene.
And for the record, you can have memory leaks in Rust too. But that's besides the point.