Recently, I tried to write a piece of code similar to the following:
pub struct Foo<'a, F> /* where F: Fn(&u32) -> bool */ {
u: &'a u32,
f: F
}
impl<'a, F> Foo<'a, F>
where F: Fn(&u32) -> bool
{
pub fn new_foo<G: 'static>(&self, g: G) -> Foo<impl Fn(&u32) -> bool + '_>
where G: Fn(&u32) -> bool
{
Foo { u: self.u, f: move |x| (self.f)(x) && g(x) }
}
}
Here, an instance of Foo represents a condition on a piece of data (the u32), where a more restrictive Foo can be built from a less restrictive one via new_foo, without consuming the old. However, the above code does not compile as written, but gives the rather cryptic error message:
error[E0308]: mismatched types
--> src/lib.rs:9:52
|
9 | pub fn new_foo<G: 'static>(&self, g: G) -> Foo<impl Fn(&u32) -> bool + '_>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected type `std::ops::FnOnce<(&u32,)>`
found type `std::ops::FnOnce<(&u32,)>`
error: higher-ranked subtype error
--> src/lib.rs:9:5
|
9 | / pub fn new_foo<G: 'static>(&self, g: G) -> Foo<impl Fn(&u32) -> bool + '_>
10 | | where G: Fn(&u32) -> bool
11 | | {
12 | | Foo { u: self.u, f: move |x| (self.f)(x) && g(x) }
13 | | }
| |_____^
error: aborting due to 2 previous errors
After much experimentation, I did find a way to make the code compile, and I believe it then functions as intended. I am used to the convention of placing bounds on impls rather than declarations when the declaration can be written without relying on those bounds, but for some reason uncommenting the where clause above, that is, copying the bound F: Fn(&u32) -> bool from the impl to the declaration of Foo itself resolved the problem. However, I don't have a clue why this makes a difference (nor do I really understand the error message in the first place). Does anyone have an explanation of what's going on here?