I wrote some code that compiled fine, then I turned T
into Option<T>
and now I'm getting this error:
error[E0308]: mismatched type...one type is more general than the other
When building a minimal case I noticed that if I change data: &str
into data: String
the code compiles fine again.
Playground link
pub trait Subscriber {
fn recv(&mut self, data: &str);
}
pub struct Task<T>(pub Option<T>)
where
T: Fn(&str);
impl<T> Subscriber for Task<T>
where
T: Fn(&str),
{
fn recv(&mut self, data: &str) {
self.0.take().unwrap()(data)
}
}
fn main() {
Task(Some(|_| ()));
}
Can someone help me understand what's going on here?
You seem to have hit a quirk in the compiler. The toilet closure (|_|()
) generalizes into a function which is generic over its input type (<T> Fn(T)
),
but in such a way that does not fulfill the intended constraint over the lifetime of T
. What we need here is a function that is generic over the lifetime of the input due to the bound for<'a> Fn(&'a str)
in the implementation of Subscriber
for Task<T>
.
(Due to the current lifetime elision rules, the higher-ranked trait bound in Subscriber
can be written without explicitly indicating for<'a>
. This expansion was made in the previous paragraph for clarity.)
This is made a bit clearer by the compiler if you try to actually use the Task
value as a Subscriber
:
let mut task = Task(Some(|_| ()));
task.recv("Message");
The error message:
error[E0599]: no method named `recv` found for struct `Task<[closure@src/main.rs:19:30: 19:36]>` in the current scope
--> src/main.rs:20:10
|
5 | / pub struct Task<T>(pub Option<T>)
6 | | where
7 | | T: Fn(&str);
| | -
| | |
| |________________method `recv` not found for this
| doesn't satisfy `_: Subscriber`
...
19 | let mut task = Task(Some(|_| ()));
| ------
| |
| doesn't satisfy `<_ as std::ops::FnOnce<(&str,)>>::Output = ()`
| doesn't satisfy `_: std::ops::Fn<(&str,)>`
20 | task.recv("Message");
| ^^^^ method not found in `Task<[closure@src/main.rs:19:30: 19:36]>`
|
= note: the method `recv` exists but the following trait bounds were not satisfied:
`<[closure@src/main.rs:19:30: 19:36] as std::ops::FnOnce<(&str,)>>::Output = ()`
which is required by `Task<[closure@src/main.rs:19:30: 19:36]>: Subscriber`
`[closure@src/main.rs:19:30: 19:36]: std::ops::Fn<(&str,)>`
which is required by `Task<[closure@src/main.rs:19:30: 19:36]>: Subscriber`
By explicitly marking the closure's single input parameter as a reference, the closure will then no longer generalize over the parameter type T
, but over a higher-ranked lifetime of the reference input 'a
in &'a _
.
let mut task = Task(Some(|_: &_| ()));
task.recv("Message");
Playground.
While not impossible, adjusting the compiler to successfully infer the right type for the original closure would possibly require substantial changes to the current monomorphization rules.
See also:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With