闭包返回函数在我的筛选器中无法正常工作

12

我无法在没有使用闭包的情况下编译它。 我试图让函数 apply 一开始就返回正确类型的闭包。

#![feature(conservative_impl_trait)]
#![allow(dead_code)]

fn accumulate<'a>(tuples: &[(&'a str, &Fn(i32) -> bool)], i: i32) {

    // this works
    let _ = tuples.iter().filter(|t| apply(second, i)(t));

    // this doesn't
    //let f = apply(second, i);
    //let _ = tuples.iter().filter(f);

    //this works as well

    let f  = |t: &&(_,_)| apply(second, i)(t);
    let _ = tuples.iter().filter(f);
}

fn apply<A, B, C, F, G>(mut f: F, a: A) -> impl FnMut(B) -> C
         where F: FnMut(B) -> G,
               G: FnMut(A) -> C,
               A: Clone
{
    move |b| f(b)(a.clone())
}


fn second<A, B: ?Sized>(&(_, ref second): &(A, B)) -> &B {
    second
}

fn main()  {}

我该怎么做才能让apply按照我的意愿工作?


根据您对我的答案的反馈,我建议您生成一个问题的[MCVE]。可能会像这样:(https://play.rust-lang.org/?gist=67472cbead98bdcdb5ea8cd92925130d)。 - Shepmaster
我将它简化为只有applysecond - 我正在尝试解决HRTBs问题。 - iopq
什么样的语言甚至不支持提取子表达式、命名它并确信它执行相同的操作,这是何等基本的操作? - Miles Rout
1个回答

13
首先,我想说这个问题与使用impl Trait语法无关。我将闭包转换为命名结构体并获得了相同的结果。
因此,让我们看一下您想要使其工作的代码:
let f = apply(second, i);
let _ = tuples.iter().filter(f);

编译器对此有何言论?
error[E0277]: the trait bound `for<'r> impl std::ops::FnMut<(&(_, _),)>: std::ops::FnMut<(&'r &(&str, &std::ops::Fn(i32) -> bool),)>` is not satisfied
  --> <anon>:11:27
   |
11 |     let _ = tuples.iter().filter(f);
   |                           ^^^^^^ trait `for<'r> impl std::ops::FnMut<(&(_, _),)>: std::ops::FnMut<(&'r &(&str, &std::ops::Fn(i32) -> bool),)>` not satisfied

error[E0277]: the trait bound `for<'r> impl std::ops::FnMut<(&(_, _),)>: std::ops::FnOnce<(&'r &(&str, &std::ops::Fn(i32) -> bool),)>` is not satisfied
  --> <anon>:11:27
   |
11 |     let _ = tuples.iter().filter(f);
   |                           ^^^^^^ trait `for<'r> impl std::ops::FnMut<(&(_, _),)>: std::ops::FnOnce<(&'r &(&str, &std::ops::Fn(i32) -> bool),)>` not satisfied

好的,我们有一个类型X,需要实现Y特质,但它没有。但是让我们仔细看一下:

for<'r> impl
std::ops::FnMut<(&(_, _),)>:
std::ops::FnMut<(&'r &(_, _),)>

啊哈!filter期望一个接受元组引用的引用的函数,而我们传递的函数接受一个元组的引用。filter传递引用是因为tuples.iter()迭代引用,并且filter传递这些引用的引用。
好的,让我们改变second的定义,使其接受引用的引用:
fn second<'a, A, B: ?Sized>(&&(_, ref second): &&'a (A, B)) -> &'a B {
    second
}

编译器仍然不满意:
error[E0277]: the trait bound `for<'r> impl std::ops::FnMut<(&&(_, _),)>: std::ops::FnMut<(&'r &(&str, &std::ops::Fn(i32) -> bool),)>` is not satisfied
  --> <anon>:11:27
   |
11 |     let _ = tuples.iter().filter(f);
   |                           ^^^^^^ trait `for<'r> impl std::ops::FnMut<(&&(_, _),)>: std::ops::FnMut<(&'r &(&str, &std::ops::Fn(i32) -> bool),)>` not satisfied

error[E0271]: type mismatch resolving `for<'r> <impl std::ops::FnMut<(&&(_, _),)> as std::ops::FnOnce<(&'r &(&str, &std::ops::Fn(i32) -> bool),)>>::Output == bool`
  --> <anon>:11:27
   |
11 |     let _ = tuples.iter().filter(f);
   |                           ^^^^^^ expected bound lifetime parameter , found concrete lifetime
   |
   = note: concrete lifetime that was found is lifetime '_#24r

expected bound lifetime parameter , found concrete lifetime的意思是什么?

f的类型是实现了FnMut(&'c &'b (&'a str, &Fn(i32) -> bool)) -> bool的某种类型。在调用apply时,B == &'c &'b (&'a str, &Fn(i32) -> bool),而C == bool。请注意,在这里B是一个固定的类型;'c代表一个固定的生命周期,被称为具体生命周期

让我们来看一下filter的签名:

fn filter<P>(self, predicate: P) -> Filter<Self, P> where
    Self: Sized, P: FnMut(&Self::Item) -> bool,

这里,P 必须实现 FnMut(&Self::Item) -> bool。实际上,这个语法是缩写形式,表示为 for<'r> FnMut(&'r Self::Item) -> bool。在这里,'r 是有约束的生命周期参数。

所以,问题在于我们实现的函数 FnMut(&'c &'b (&'a str, &Fn(i32) -> bool)) -> bool 实现了 for<'r> FnMut(&'r Self::Item) -> bool。我们需要一个实现了 for<'c> FnMut(&'c &'b (&'a str, &Fn(i32) -> bool)) -> bool 的函数。目前唯一的解决方法是像这样编写 apply 函数:

fn apply<A, B, C, F, G>(mut f: F, a: A) -> impl FnMut(&B) -> C
         where F: FnMut(&B) -> G,
               G: FnMut(A) -> C,
               A: Clone
{
    move |b| f(b)(a.clone())
}

或者更明确的版本:
fn apply<A, B, C, F, G>(mut f: F, a: A) -> impl for<'r> FnMut(&'r B) -> C
         where F: for<'r> FnMut(&'r B) -> G,
               G: FnMut(A) -> C,
               A: Clone
{
    move |b| f(b)(a.clone())
}

如果Rust最终支持高种类类型,那么可能会有一种更优雅的解决问题的方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接