Dan Jenson
Dan Jenson

Reputation: 1041

Passing additional state to rust `hyper::service::service_fn`

I am having trouble passing additional state to my service function, but I can't sort out the lifetimes in the closures. None of the tutorials seem to address it:

https://hyper.rs/

https://docs.rs/hyper/0.13.4/hyper/server/index.html

https://docs.rs/hyper/0.13.4/hyper/service/fn.make_service_fn.html

Relevant code:

#[tokio::main]                                                                       
async fn main() {                                                               
    let addr = ([127, 0, 0, 1], 8080).into();                                   
    let db = Arc::new(Mutex::new(Slab::new()));                                 
    let server = Server::bind(&addr).serve(make_service_fn(|_conn| async {      
        let db = db.clone();                                                         
        Ok::<_, Infallible>(service_fn(move |req| serve_req(req, &db)))         
    }));                                                                             
    if let Err(e) = server.await {                                                   
        eprintln!("server error: {}", e)                                             
    }                                                                                
}

Errors:

   Compiling hyper-microservice v0.1.0 (/data/repos/rust/hyper-microservice)
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
   --> src/main.rs:121:66
    |
121 |         Ok::<_, Infallible>(service_fn(move |req| serve_req(req, &db)))
    |                                                                  ^^^
    |
note: first, the lifetime cannot outlive the lifetime `'_` as defined on the body at 121:40...
   --> src/main.rs:121:40
    |
121 |         Ok::<_, Infallible>(service_fn(move |req| serve_req(req, &db)))
    |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that closure can access `db`
   --> src/main.rs:121:66
    |
121 |         Ok::<_, Infallible>(service_fn(move |req| serve_req(req, &db)))
    |                                                                  ^^^
note: but, the lifetime must be valid for the expression at 121:29...
   --> src/main.rs:121:29
    |
121 |         Ok::<_, Infallible>(service_fn(move |req| serve_req(req, &db)))
    |                             ^^^^^^^^^^

My understanding is that make_service_fn is responsible for returning a ServiceFn that can be used to serve requests for a given connection, and it creates a new ServiceFn for every connection. Now, service_fn takes a function/closure that that takes a request and returns a Future that returns a response (or error). And, as I understand it, the ServiceFn may be executed multiple times per connection. So roughly, each client gets it's own thread, which executes an async service function for each request on that connection (so the same client could make multiple requests on the same connection and be served concurrently within the same thread).

I believe that what might be happening is that the compiler thinks that one of the closures might outlive db?

Upvotes: 8

Views: 2284

Answers (1)

vaassi
vaassi

Reputation: 51

The correct code should be:

#[tokio::main]
async fn main() {
    let addr = ([127, 0, 0, 1], 8080).into();
    let db = Arc::new(Mutex::new(Slab::new()));
    let server = Server::bind(&addr).serve(make_service_fn(move |_conn| {
        let db = db.clone();
        async move { Ok::<_, Infallible>(service_fn(move |req| serve_req(req, db.clone()))) }
    }));

    if let Err(e) = server.await {
        eprintln!("server error: {}", e)
    }
}

Upvotes: 5

Related Questions