简体   繁体   中英

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

I am trying to write a function that takes a slice of functions. Consider the following simple 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()][..]);
}

It fails to compile:

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)

Is there any way to do this?

Your problem is that every element of the array must be of the same type, but the return of a function declared as returning impl Trait is an opaque type , that is an unspecified, unnamed type, that you can only use by means of the given trait.

You have two functions that return the same impl Trait but that does not mean that they return the same type. In fact, as your compiler shows, they are different opaque types, so they cannot be part of the same array. If you were to write an array of values of the same type, such as:

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

then it would work. But with different functions, there will be different types and the array is impossible to build.

Does that mean there is no solution for your problem? Of course not! You just have to invoke dynamic dispatch. That is you have to make your slice of type &[&dyn Fn(&str) -> usize] . For that you need to do two things:

  1. Add a level of indirection: dynamic dispatching is always done via references or pointers ( &dyn Trait or Box<dyn Trait> instead of Trait ).
  2. Do an explicit cast to the &dyn Trait to avoid ambiguities in the conversion.

There are many ways to do the cast: you can cast the first element of the array, or you can declare the temporary variables, or give the slice a type. I prefer the latter, because it is more symmetric. Something like this:

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

Link to a playground with this solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM