I've constructed a closure example that I can't get to work, nor can I find any reason why it shouldn't work. Why does it fail to compile on the last closure?
struct S {}
fn filter<P>(predicate: P)
where
P: Fn(&S) -> bool,
{
predicate(&S {});
}
fn main() {
// this works
filter(|_s| true);
// this also works
fn cb1(_s: &S) -> bool {
true
}
filter(cb1);
// but this doesn't work
let cb2 = |_s| true;
filter(cb2);
}
Output:
error[E0631]: type mismatch in closure arguments
--> /tmp/closure.rs:19:5
|
18 | let cb2 = |_s| true;
| --------- found signature of `fn(_) -> _`
19 | filter(cb2);
| ^^^^^^ expected signature of `for<'r> fn(&'r S) -> _`
|
note: required by `filter`
--> /tmp/closure.rs:3:1
|
3 | fn filter<P>(predicate: P) where P: Fn(&S) -> bool,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0271]: type mismatch resolving `for<'r> <[closure@/tmp/closure.rs:18:15: 18:24] as std::ops::FnOnce<(&'r S,)>>::Output == bool`
--> /tmp/closure.rs:19:5
|
19 | filter(cb2);
| ^^^^^^ expected bound lifetime parameter, found concrete lifetime
|
note: required by `filter`
--> /tmp/closure.rs:3:1
|
3 | fn filter<P>(predicate: P) where P: Fn(&S) -> bool,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The approach from Shepmaster's answer can be applied to achieve this goal. Like in that answer, we can define a generic
constrain
function requiring a type with the kind of lifetime bounds that will be required to satisfyfilter
. We apply the function to the closure and then store it to. Note that callingconstrain(cb)
when invokingfilter
(which was my first attempt) doesn't work because the compiler cannot infer the type of the closure variable when it is passed toconstrain
any more than it could when it was being passed tofilter
.The invocation of
constrain
has no effect at run-time, it just guides the compiler to infer the lifetime bounds for the variable that we need forfilter
. This allows storing the closure and callingfilter
without modifying its signature:Playground.
From following Type mismatches resolving a closure that takes arguments by reference and How to declare a lifetime for a closure argument? it appears the solution is to change:
to
Though I'm not sure why. It seems to be related to inferred lifetimes when a closure is specified inline vs when it is stored in a variable and used later. But it's unclear why
&S
needs an'a
lifetime, when&S
is not a result that is returned. If you understand this, please explain in a comment.Though this question is "solved", the trimmed-down failure case posted originally does not actually help my true problem, because I cannot edit the source of the code I am having trouble with https://docs.rs/walkdir/2.2.9/walkdir/struct.IntoIter.html#method.filter_entry
The issue manifested when I tried to pass a stored callback into the
filter_entry
method. The solution would be to put in explicit lifetimes in thefilter_entry
signature, as described earlier in this post, but you can only do that if you want to edit the third party code. I think unfortunately the answer for that particular problem is "you can't use a stored closure withfilter_entry
"