rustmethodstypesclosureshigher-order-functions

Rust higher order method?


If I wanted to generate a polynomial function in Rust, one way to do it is the following,

fn new_polynomial(vec: Vec<i32>) -> impl Fn(i32) -> i32 {
    move |x| vec.iter().fold(0, |acc, a| x * acc + a)
}

But, since I want to define lots of higher-order functions (find roots, differentiate, integrate, etc.), I would like to have these functions implemented as methods. The code that I have is as follows,

trait Function {
    fn new_polynomial(vec: Vec<i32>) -> Self;
    fn eval(&self, x: i32) -> i32;
}

impl<F> Function for F
where
    F: Fn(i32) -> i32,
{
    fn new_polynomial(vec: Vec<i32>) -> Self {
        move |x| vec.iter().fold(0, |acc, a| x * acc + a)
    }

    fn eval(&self, x: i32) -> i32 {
        self(x)
    }
}

and produces the following E308,

error[E0308]: mismatched types
  --> src/main.rs:14:9
   |
9  | impl<F> Function for F
   |      - expected this type parameter
...
13 |     fn new_polynomial(vec: Vec<i32>) -> Self {
   |                                         ---- expected `F` because of return type
14 |         move |x| vec.iter().fold(0, |acc, a| x * acc + a)
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter `F`, found closure
   |
   = note: expected type parameter `F`
                     found closure `{closure@src/main.rs:14:9: 14:17}`
   = help: every closure has a distinct type and so could not always match the caller-chosen type of parameter `F`

Hopefully I'm not missing something obvious here.

Thanks.


Solution

  • If you want a method on vectors that converts them to a polynomial, you should implement the trait on the vectors, not on the functions:

    trait ToPolynomial {
        fn new_polynomial(self) -> impl Fn(i32) -> i32;
    }
    
    impl ToPolynomial for Vec<i32> {
        fn new_polynomial(self) -> impl Fn(i32) -> i32 {
            move |x| self.iter().fold(0, |acc, a| x * acc + a)
        }
    }
    

    The Function trait should not contain this method:

    trait Function {
        fn eval(&self, x: i32) -> i32;
    }
    
    impl<F> Function for F
    where
        F: Fn(i32) -> i32,
    {
        fn eval(&self, x: i32) -> i32 {
            self(x)
        }
    }
    

    You would use these traits like this:

    let polynomial = vec![1,2,3].new_polynomial();
    println!("{}", polynomial.eval(2));
    

    playground