Skip to content

Confusing diagnostic for closures #147264

@VorpalBlade

Description

@VorpalBlade

Code

use std::io::Read;

pub trait HttpClient {
    fn get(&self, url: &str) -> Result<Box<dyn Read>, std::io::Error>;
}

struct DummyHttpClient<T> {
    responder: T,
}

impl<T> HttpClient for DummyHttpClient<T>
where
    T: Fn(&str) -> Result<Box<dyn Read>, std::io::Error>,
{
    fn get(&self, url: &str) -> Result<Box<dyn Read>, std::io::Error> {
        (self.responder)(url)
    }
}

struct UserBuilder {
    client: Option<Box<dyn HttpClient>>,
}

impl UserBuilder {
    pub fn new() -> Self {
        Self { client: None }
    }

    /// Set the HTTP client to use for requests.
    pub fn http_client(mut self, client: Box<dyn HttpClient>) -> Self {
        self.client = Some(client);
        self
    }
}

fn test_custom_http_client() {
    let http_client = Box::new(DummyHttpClient {
        responder: |url| {
            Err(std::io::Error::new(
                std::io::ErrorKind::Other,
                format!("DummyHttpClient cannot fetch {url}"),
            ))
        },
    });
    let client = UserBuilder::new().http_client(http_client);
}

Current output

error: implementation of `FnOnce` is not general enough
  --> src/lib.rs:45:49
   |
45 |     let client = UserBuilder::new().http_client(http_client);
   |                                                 ^^^^^^^^^^^ implementation of `FnOnce` is not general enough
   |
   = note: closure with signature `fn(&'2 str) -> Result<Box<dyn std::io::Read>, std::io::Error>` must implement `FnOnce<(&'1 str,)>`, for any lifetime `'1`...
   = note: ...but it actually implements `FnOnce<(&'2 str,)>`, for some specific lifetime `'2`

error: implementation of `Fn` is not general enough
  --> src/lib.rs:45:49
   |
45 |     let client = UserBuilder::new().http_client(http_client);
   |                                                 ^^^^^^^^^^^ implementation of `Fn` is not general enough
   |
   = note: closure with signature `fn(&'2 str) -> Result<Box<dyn std::io::Read>, std::io::Error>` must implement `Fn<(&'1 str,)>`, for any lifetime `'1`...
   = note: ...but it actually implements `Fn<(&'2 str,)>`, for some specific lifetime `'2`

Desired output

It should suggest that adding explicit type annotation &str to the argument of the closure would fix the issue. Or that you could add

  where
    T: Fn(&str) -> Result<Box<dyn Read>, std::io::Error>,

to the struct itself. That also fixes the issue.

Rationale and extra context

The current diagnostic is highly confusing and not helpful.

Other cases

Rust Version

❯ rustc --version --verbose
rustc 1.90.0 (1159e78c4 2025-09-14)
binary: rustc
commit-hash: 1159e78c4747b02ef996e55082b704c09b970588
commit-date: 2025-09-14
host: x86_64-unknown-linux-gnu
release: 1.90.0
LLVM version: 20.1.8

Anything else?

Originally reported here: https://users.rust-lang.org/t/strange-type-inference-around-fnonce-fn/134342

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions