为什么这个简单的闭包失败了,而其他两个函数却成功了?



我构造了一个闭包示例,我无法开始工作,也找不到任何理由为什么它不应该工作。 为什么它在最后一次闭包时编译失败?

操场

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);
}

输出:

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,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

从以下类型不匹配解决通过引用获取参数的闭包和 如何声明闭包参数的生存期? 看来解决方案是改变:

fn filter<P>(predicate: P)
where
P: Fn(&S) -> bool,
{
predicate(&S {});
}

fn filter<'a, P>(predicate: P)
where
P: Fn(&'a S) -> bool,
{
predicate(&S {});
}

虽然我不知道为什么。 当内联指定闭包时,它似乎与推断的生命周期有关,当它存储在变量中并在以后使用时。 但目前还不清楚为什么&S需要一个'a的生命周期,而&S不是返回的结果。 如果您理解这一点,请在评论中解释。

虽然这个问题已经"解决",但最初发布的精简失败案例实际上并没有帮助我真正的问题,因为我无法编辑我遇到问题的代码源代码 https://docs.rs/walkdir/2.2.9/walkdir/struct.IntoIter.html#method.filter_entry

当我尝试将存储的回调传递到filter_entry方法时,出现了这个问题。 解决方案是在filter_entry签名中加入显式生存期,如本文前面所述,但只有在要编辑第三方代码时才能这样做。 我认为不幸的是,该特定问题的答案是"您不能将存储的闭包与filter_entry一起使用">

我认为

不幸的是,该特定问题的答案是"您不能将存储的闭包与filter_entry一起使用">

Shepmaster的回答可以应用这种方法来实现这个目标。就像在那个答案中一样,我们可以定义一个泛型constrain函数,需要一个具有满足filter所需的生存期边界的类型。我们将函数应用于闭包,然后将其存储到。请注意,在调用filter(这是我的第一次尝试)时调用constrain(cb)不起作用,因为编译器在传递给constrain时无法推断闭包变量的类型,就像它传递给filter时无法推断的类型一样。

constrain的调用在运行时没有任何效果,它只是指导编译器推断我们需要filter的变量的生命周期边界。这允许在不修改其签名的情况下存储闭包和调用filter

fn constrain<F>(fun: F) -> F
where
F: for<'a> Fn(&'a S) -> bool,
{
fun
}
fn main() {
let cb = constrain(|_s| true);
filter(cb);
}

操场。

最新更新