Hyper, routing, and `!Sync` futures

I'm building an HTTP api with hyper and I'm having trouble with dynamic routing.
In the beginning I only had static routes, my routing was done by a simple match:

async fn route(req: hyper::Request<hyper::Body>) -> hyper::Response<hyper::Body> {
    match (req.method(), req.uri().path) {
        (&hyper::Method::GET, "/version") => unimplemented!(),
        // etc

This worked great until I needed to add dynamic routes (e.g /post/1234). I found the httprouter crate but it requires route handlers to be Sync. Most of my route handlers are !Sync since I use reqwest and async_trait all of which returns !Sync futures.
I dived into httprouter source code and found that route handlers must be Sync because otherwise you can't wrap a Router (which owns the handlers) in an Arc to use it across connections.

The easy fix would be to create a clone of the router for each connection but this seems wildly inefficient.

How would you do it? Is it even possible with !Sync futures?

You can use sync_wrapper (or similar types) to get back Sync.

I know about sync_wrapper but I couldn't make it work. Since SyncWrapper::get_mut() takes a &mut self I can't use it wrapped in an Arc. I feel like I'm missing something obvious.

You would never put a future in an Arc.

You should never need a Future to be Sync, since this requirement only comes up when wrapping the Future in a &Fut-yielding "handle", such as &Fut itself or Arc<Fut>, etc., which are (almost) useless because Future's API (the inner .poll() function) requires Pin<&mut …> access (or, a fortiori, owned access).

If you need shared ownership over a Future, there is the .shared() adapter() from the ::futures crate, which yields a cheaply Cloneable Future :slightly_smiling_face:


Thanks for all the answers, it helped me a lot. I wrote my own router from scratch and it's working great !

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.