Async fn pointer for hyper service

I'm trying to split my code up into multiple crates to organize things better. I'm using hyper for a local server that listens on multiple ports. When everything was in the one crate, I didn't have a problem. Now that I'm trying to split things apart, I've hit a problem trying to pass an async fn pointer to another function. Here is what I have so far:

use hyper;

fn new_local_handler<F>(port: u16, callback: fn(hyper::Request<hyper::Body>) -> F)
    where
        F: std::future::Future<Output = hyper::Response<hyper::Body>> + Send
{
    let addr = std::net::SocketAddr::from(([127, 0, 0, 1], port));

    let make_svc = hyper::service::make_service_fn(|_conn| async {
        Ok::<_, std::convert::Infallible>(hyper::service::service_fn(
            |req: hyper::Request<hyper::Body>| async {
                Ok::<_, std::convert::Infallible>(callback(req).await)
            }
        ))
    });

    hyper::Server::bind(&addr).serve(make_svc);
}

This function doesn't compile, and the compiler suggests adding a 'static bound to the generic in addition to the Send bound (which it also suggested).

However if I add that 'static bound I then get a different error pointing to the closure passed to make_service_fn complaining that callback doesn't live long enough, and that it needs to be 'static.

I don't think I want to add the 'static bound in this case, as the requests and responses won't have static lifetimes.

Any help would be greatly appreciated.

(I realize this function also should return the hyper server it creates, but that should be easy once I solve this other problem)

Try this

use hyper;

fn new_local_handler<Fut>(port: u16, callback: fn(hyper::Request<hyper::Body>) -> Fut)
where
    Fut: std::future::Future<Output = hyper::Response<hyper::Body>>,
    Fut: Send + 'static,
{
    let addr = std::net::SocketAddr::from(([127, 0, 0, 1], port));

    let make_svc = hyper::service::make_service_fn(move |_conn| {
        let service = hyper::service::service_fn(move |req| {
            let fut = callback(req);
            async move { Ok::<_, std::convert::Infallible>(fut.await) }
        });
        async move { Ok::<_, std::convert::Infallible>(service) }
    });

    hyper::Server::bind(&addr).serve(make_svc);
}

You do want the 'static bound here.

1 Like

Thank you, that appears to work correctly!

Would you mind explaining your answer a little bit?

  1. Why is the 'static bound needed?
  2. I still don't quite understand async blocks. I do know that blocks are expressions in rust. Does making an async block just wrap whatever value the block would normally return in a Future?

The 'static bound is needed to ensure that your future is safe to move across threads without keeping track of cross-thread references.

As for async blocks, you can think of them as a lazily evaluated value. The contents of the block are executed once the block is .awaited, but not before then. If it's passed around in a variable, then the contents have not yet begun executing.

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.