如何编写需要一部分函数的函数?

时间:2019-09-15 22:00:15

标签: function rust higher-order-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)

有什么办法吗?

1 个答案:

答案 0 :(得分:3)

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

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

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

使用此解决方案链接到playground