如何编写一个接受函数片段的函数?

How to write a function that takes a slice of functions?

我正在尝试编写一个采用函数片段的函数。考虑以下简单的 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)

有什么办法吗?

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

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

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

那就可以了。但是不同的功能,就会有不同的类型,数组是无法搭建的。

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

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

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

fn main() {
    let fns: &[&dyn Fn(&str) -> usize] = 
        &[&f1(), &f2()];
    g(fns);
}
使用此解决方案

Link 到 playground