Confusing diagnostic for closures
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
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
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the example in src/lib.rs with the reported rustc 1.90.0 toolchain and start at the diagnostic emitted on line 45. Trace the compiler diagnostic path for the not-general-enough Fn and FnOnce messages; done means the output explains the lifetime mismatch and suggests annotating the closure argument or constraining T on the struct.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100