【问题标题】:How to write a function that takes a slice of functions?如何编写一个需要一片函数的函数?
【发布时间】:2019-09-15 22:00:15
【问题描述】:

我正在尝试编写一个采用函数切片的函数。考虑以下简单的illustration

fn g<P: Fn(&str) -> usize>(ps: &[P]) { }

fn f1() -> impl Fn(&str) -> usize { |s: &str| s.len() }
fn f2() -> impl Fn(&str) -> usize { |s: &str| s.len() }

fn main() {
    g(&[f1(), f2()][..]);
}

编译失败:

error[E0308]: mismatched types
 --> src/main.rs:6:15
  |
6 |     g(&[f1(), f2()][..]);
  |               ^^^^ expected opaque type, found a different opaque type
  |
  = note: expected type `impl for<'r> std::ops::Fn<(&'r str,)>` (opaque type)
             found type `impl for<'r> std::ops::Fn<(&'r str,)>` (opaque type)

有什么办法吗?

【问题讨论】:

    标签: function rust higher-order-functions


    【解决方案1】:

    您的问题是数组的每个元素都必须属于同一类型,但声明为返回 impl Trait 的函数的返回是一个不透明类型,即未指定的未命名类型,你只能通过给定的特征来使用。

    您有两个函数返回相同的impl Trait,但这并不意味着它们返回相同的类型。事实上,正如您的编译器所示,它们是不同的不透明类型,因此它们不能属于同一个数组。如果你要写一个相同类型的值数组,比如:

        g(&[f1(), f1(), f1()]);
    

    然后它会工作。但是不同的函数,会有不同的类型,数组是不可能构建的。

    这是否意味着您的问题没有解决方案?当然不是!你只需要调用动态调度。也就是说,您必须制作&amp;[&amp;dyn Fn(&amp;str) -&gt; usize] 类型的切片。为此,您需要做两件事:

    1. 添加一个间接级别:动态调度总是通过引用或指针完成(&amp;dyn TraitBox&lt;dyn Trait&gt; 而不是Trait)。
    2. &amp;dyn Trait 进行显式强制转换以避免转换中的歧义。

    有很多方法可以进行转换:你可以转换数组的第一个元素,或者你可以声明临时变量,或者给切片一个类型。我更喜欢后者,因为它更对称。像这样的:

    fn main() {
        let fns: &[&dyn Fn(&str) -> usize] = 
            &[&f1(), &f2()];
        g(fns);
    }
    

    使用此解决方案链接到 playground

    【讨论】:

      猜你喜欢
      • 2015-07-03
      • 2018-12-07
      • 2023-04-09
      • 2011-06-10
      • 2012-12-25
      • 1970-01-01
      • 1970-01-01
      • 2018-08-30
      • 1970-01-01
      相关资源
      最近更新 更多