The implementation of FnOnce is not general enough

In a previous question I had the following code using hyper:

use std::{
    convert::Infallible,
    net::SocketAddr,
    sync::{Arc, Mutex},
};

use hyper::{
    header,
    service::{make_service_fn, service_fn},
    Body, Method, Request, Response, Server,
};

// I'm only using a usize here for demonstration purposes.
async fn handle(data: Arc<Mutex<usize>>, req: Request<Body>) -> Result<Response<Body>, Infallible> {
    let data = *data.lock().unwrap();
    Ok(Response::new(data.to_string().into()))
}

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

In an effort to better understand what's going on, I made the following (working) changes:

    let make_svc = make_service_fn(|_conn| {
        let data = data.clone();
        let handle = move |req| handle(data.clone(), req);
        let service = service_fn(handle);
        async { Ok::<_, Infallible>(service) }
    });

but if I take it a step further:

    let make_service = |_conn| {
        let data = data.clone();
        let handle = move |req| handle(data.clone(), req);
        let service = service_fn(handle);
        async { Ok::<_, Infallible>(service) }
    };
    let make_svc = make_service_fn(make_service);

I get an error that the implementation of FnOnce is not general enough. I don't understand why these two refactorings would be different. Can anyone shed some light on this?

The exact error is:

error: implementation of `FnOnce` is not general enough
  --> src/main.rs:84:21
   |
84 |     if let Err(e) = server.await {
   |                     ^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
   |
   = note: closure with signature `fn(&'2 AddrStream) -> impl Future<Output = [async output]>` must implement `FnOnce<(&'1 AddrStream,)>`, for any lifetime `'1`...
   = note: ...but it actually implements `FnOnce<(&'2 AddrStream,)>`, for some specific lifetime `'2`

Thanks

rustc is bad at figuring out closures should be higher ranked unless the closure is declared in a context demanding it. A properly bounded function parameter is one such context. You moved the closure out of that context.

This might be enough to fix the example (I didn't try it).

-    let make_service = |_conn| {
+    let make_service = |_conn: &_| {

(You may need to fill in _.)

3 Likes

See also:

2 Likes

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.