Satisfying trait bounds for boxed Fn traits and boxed closures: for<'r>?


#1

I am trying to implement a trait ODE for all types F: Fn(&f64) -> f64, so that I can define an integration on named functions and also closures. While my implementation works quite well for named functions, I get quite complex errors for boxed closures:

the trait bound `for<'r> std::boxed::Box<for<'r> std::ops::Fn(&'r f64) -> f64>: std::ops::Fn<(&'r f64,)>` is not satisfied

This can be reproduced with this code example on playpen: https://is.gd/Adx4bt

Can somebody explain to me what exactly the error here is? How do I go about fixing this?

Playpen code also copied below.

struct Euler<S, T> {
    dt: f64,
    system: S,
    temp: T,
}

impl<S,T> Euler<S,T>
    where S: ODE<State=T>,
          T: Clone,
{
    pub fn new(mut system: S, dt: f64, state: &T) -> Self {
        let temp = system.differentiate(&state);

        Euler {
            dt: dt,
            system: system,

            temp: temp,
        }
    }
}

pub trait ODE {
    type State: Clone;
    
    fn differentiate_into(&mut self, &Self::State, &mut Self::State);

    fn differentiate(&mut self, state: &Self::State) -> Self::State {
        let mut derivative = state.clone();
        self.differentiate_into(state, &mut derivative);
        derivative
    }
}

impl<F> ODE for F
    where F: Fn(&f64) -> f64,
{
    type State = f64;

    fn differentiate_into(&mut self, state: &Self::State, derivative: &mut Self::State) {
        *derivative = self(state);
    }
}

fn main() {
    fn fn_system(x: &f64) -> f64 {
        1.0 * x
    }
    
    let a = 1.0;
    let closure_system: Box<Fn(&f64) -> f64> = Box::new(|x: &f64| { a * x});
    
    let mut x = 1.0;
    let timestep = 0.1;

    // // Named functions work!
    // let mut stepper = Euler::new(fn_system, timestep, &x);
    let mut stepper = Euler::new(closure_system, timestep, &x);
}


#2

There is a limitation that Box<Fn> doesn’t implement the Fn trait. That’s because Fn requires FnOnce, which afaik is currently impossible to implement for Box<Fn>.

The simplest workaround is not to use Box here – for example passing just a closure or passing &Fn(&f64) -> f64 works:

    let closure_system = |x: &f64| { a * x}; // or
    let closure_system: &Fn(&f64) -> f64 = &|x: &f64| { a * x};

The other workaround is to provide a manual impl of ODE for Box<Fn>:

impl ODE for Box<Fn(&f64) -> f64>
{
    type State = f64;
    
    fn differentiate_into(&mut self, state: &Self::State, derivative: &mut Self::State) {
        *derivative = self(state)
    }
}

The Box defaults to + 'static though, so you need a move in main:

    let closure_system: Box<Fn(&f64) -> f64> = Box::new(move |x: &f64| { a * x});

If you don’t want to limit boxes to 'static, you can modify the impl to (full playground):

impl<'a> ODE for Box<Fn(&f64) -> f64 + 'a> { ... }

That way you don’t have to change main at all. Although if the boxed closure borrows something from the environment anyway and the environment has to be kept alive, it’s ususally simpler to just use & instead of Box.


#3

This is great, thank you so much! In fact, I wanted to impl ODE for the trait object, but I made a crucial mistake:

impl<F> ODE for Box<F>
    where F: Fn(&f64) -> f64
{
    type State = f64;
    
    fn differentiate_into(&mut self, state: &Self::State, derivative: &mut Self::State) {
        *derivative = self(state)
    }
}

And of course this breaks: if F: Fn(T) -> T, then also Box<F>: Fn(T) -> T which gave me conflicting trait implemenations.