Boxed function factory (higher order function) in Rust results in "call expression requires function"

Viewed 294

I want to pass a function factory, i.e. a higher order function, but I get call expression requires function errors.

// function that takes 4 f32 generates a function which maps one f32 to another f32
// DOESN'T WORK
type FunctionFactoryType = Box<dyn Fn(f32, f32, f32, f32) -> (dyn Fn(f32) -> f32)>;

fn abc(x: FunctionFactoryType) {
    (*x)(1.0, 2.0, 3.0, 4.0);
    // ^ call expression requires function
    // expected function, found `(dyn Fn(f32, f32, f32, f32) -> (dyn Fn(f32) -> f32 + 'static) + 'static)`
}

If I use a "regular function" (not higher order), i.e. Box<dyn Fn(f32 -> f32)> it works. Am I missing something or is this a Rust error?

2 Answers

Your call compiles if you make the outer dyn Fn return Box<dyn Fn> instead of dyn Fn directly:

type FunctionFactoryType = Box<dyn Fn(f32, f32, f32, f32) -> Box<dyn Fn(f32) -> f32>>;

(Note that you don't need (*x)(...) to call a Box<Fn...>, you can just use x(...).)

The return type of dyn Fn(...) isn't valid because dyn Fn is unsized, you have to put it behind a reference or a smart pointer. Since it's in return position, it can't be a reference, so Box<dyn Fn(f32) -> f32> is what you want. (This is also covered in the book.)

The error message is quite cryptic, presumably as a result of the flaw in the declaration preventing Box from implementing Fn(...), which made it a non-function. Interestingly, if you comment out the call altogether, compilation succeeds even with the original incorrect definition of FunctionFactoryType.

I think the problem is that the return type of your function is not Sized, e.g. this works:

type FunctionFactoryType<O: Fn(f32) -> f32> = Box<dyn Fn(f32, f32, f32, f32) -> O>;

fn abc<O: Fn(f32) -> f32>(x: FunctionFactoryType<O>) {
    (*x)(1.0, 2.0, 3.0, 4.0);
}

It also works to put the returned function into a Box<Fn(f32) -> f32>.

I can't answer why it states this isn't a function expressions.

Related