"implementation of From is not general enough" with hyper

I've managed to reduce this test case to eliminate all dependencies, including hyper, and now it's just 68 lines. playground (I know, that's still a lot. If you prefer, here's another "minimized" test case that's only 62 lines... plus all of hyper and http and http-body and hyper-util and http-body-util and tokio...)

In short:

// full code at: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e2f0034445016fad21139afa1d06d65e
async fn handle_connection() {
    serve_connection(ServiceFn {
        f: || async { Err::<Incoming, GenericError>("failed".into()) },
    })
    .await;
}

pub async fn server_main() {
    spawn(handle_connection()); // <--- ERROR
}

The error message arises when Rust tries to prove that the future returned by handle_connection() is Send. Rust says:

error: implementation of `From` is not general enough
  --> src/lib.rs:67:5
   |
67 |     spawn(handle_connection());
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `From` is not general enough
   |
   = note: `Box<(dyn std::error::Error + Send + Sync + 'static)>` must implement `From<Box<(dyn std::error::Error + Send + Sync + '0)>>`, for any lifetime `'0`...
   = note: ...but it actually implements `From<Box<(dyn std::error::Error + Send + Sync + 'static)>>`
  1. Can anyone explain the message?
  2. Is it a rustc bug that the error message doesn't point to the Send bound that led to the trouble?

Hmm. It really seems to have a problem with the async block in that closure. Its like it loses the requirement of GenericError to be a Box<dyn ... + 'static> and decides the lifetime can be dependent on something else. If you make this part less generic:

impl<F, Ret> Service for ServiceFn<F>
where
    F: Fn() -> Ret,
    Ret: Future<Output = Result<Incoming, GenericError>>,
    // E: Into<GenericError>,
{

You get errors like

   = note: expected enum `Result<_, Box<dyn std::error::Error + Send + Sync>>`
              found enum `Result<_, Box<(dyn std::error::Error + Send + Sync + 'static)>>`

(Example with other random changes I threw at it.)

Feels like a bug to me.


I did find a workaround: use an async fn instead of || async {}.

async fn handle_connection() {
    async fn cmon_now() -> Result<Incoming, GenericError> {
        Err::<Incoming, GenericError>("failed".into())
    }
    
    serve_connection(ServiceFn { f: cmon_now }).await;
}

I think this is the bug.

(Some of the gymnastic I tried resulted in the sparse error: higher-ranked lifetime error unless I went back to version 1.62, i.e. pre-full-NLL-stabilization. (But I've since thrown that code out and don't think it's important enough to reproduce.))

2 Likes

I've seen that message too.

OK, good to know I'm not the only one. Yikes.

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.