【问题标题】:Function returning a closure not working inside my filter返回闭包的函数在我的过滤器中不起作用
【发布时间】:2016-09-16 23:19:14
【问题描述】:

如果不使用闭包,我无法编译它。我试图让函数 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 像我想要的那样工作?

【问题讨论】:

  • 根据您对我的回答的反馈,我建议您针对您的问题生成一个minimal reproducible example。可能是something like this
  • 我将其缩减为 applysecond - 我正在尝试解决 HRTB 问题。
  • 什么样的语言甚至不支持提取子表达式、命名它以及确信它会做同样的事情这样的基本操作?

标签: rust closures trait-objects


【解决方案1】:

首先,让我说这个问题与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(&amp;'c &amp;'b (&amp;'a str, &amp;Fn(i32) -&gt; bool)) -&gt; bool 的某种类型。在对applyB == &amp;'c &amp;'b (&amp;'a str, &amp;Fn(i32) -&gt; 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(&amp;Self::Item) -&gt; bool。实际上,这个语法是for&lt;'r&gt; FnMut(&amp;'r Self::Item) -&gt; bool 的简写。这里。 'r 是绑定的生命周期参数。

所以,问题在于我们实现FnMut(&amp;'c &amp;'b (&amp;'a str, &amp;Fn(i32) -&gt; bool)) -&gt; bool 的函数没有实现for&lt;'r&gt; FnMut(&amp;'r Self::Item) -&gt; bool。我们需要一个实现for&lt;'c&gt; FnMut(&amp;'c &amp;'b (&amp;'a str, &amp;Fn(i32) -&gt; bool)) -&gt; 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 最终支持 higher-kinded types可能会有更优雅的方式来解决这个问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多