# Function taking another function as argument in a generic struct

#1

Hi everyone,

I am writing a library whose users will be able to specify one function to compute some values and a second function to call on the first one. My library will provide a struct to hold both and simplify the calculations.

I am building this step by step. First, I write two standalone functions and this works:

``````fn double(x: f32) -> f32 {
x * 2.0
}

fn do_call<F: Fn(f32) -> f32>(f: F, x: f32) -> f32 {
f(x)
}

fn main() {
println!("{}", do_call(double, 3.0));
println!("{}", do_call(&double, 3.0)) //Note that & also works
}
``````

Let’s wrap this inside a struct:

``````fn dbl(x: f32) -> f32 {
x * 2.0
}

fn do_call<F: Fn(f32) -> f32>(f: F, x: f32) -> f32 {
f(x)
}

struct Lazy<F, G>
where F: Fn(f32) -> f32,
G: Fn(F, f32) -> f32
{
callee: F,
caller: G
}

impl<F, G> Lazy<F, G>
where F: Fn(f32) -> f32,
G: Fn(F, f32) -> f32
{
fn call(&self, x: f32) -> f32 {
(self.caller)(&self.callee, x)
}
}

fn main() {
let l = Lazy{callee: dbl, caller: do_call};

println!("{}", l.call(3.0));
}
``````

This fails with:

``````error[E0308]: mismatched types
--> <anon>:22:23
|
22 |         (self.caller)(&self.callee, x)
|                       ^^^^^^^^^^^^ expected type parameter, found &F
|
= note: expected type `F`
= note:    found type `&F`

error: aborting due to previous error
``````

Why does & not work now? The bounds on F are the same, as far as I can see.

Thanks.

#2

The first snippet works because there are two different specializations of do_call generated - one that accepts a `Fn(f32) -> f32` argument, and one that accepts a `&Fn(f32) -> f32` argument. This doesn’t work in the second snippet, because the types of F and G in `Lazy::call` are determined by the types originally stored in the `Lazy` struct, so there is a mismatch when you try to pass a different type as an argument.

You can work around the problem you’re seeing by adding the `+ Copy` bound to both F and G for `Lazy::call` and remove the `&` from `&self.callee`.

#3

I wonder if the better formulation is the following:

``````fn dbl(x: f32) -> f32 {
x * 2.0
}

fn do_call<F>(f: &F, x: f32) -> f32 where F: Fn(f32) -> f32 {
f(x)
}

struct Lazy<F, G>
where F: Fn(f32) -> f32,
G: Fn(&F, f32) -> f32
{
callee: F,
caller: G
}

impl<F, G> Lazy<F, G>
where F: Fn(f32) -> f32,
G: Fn(&F, f32) -> f32
{
fn call(&self, x: f32) -> f32 {
(self.caller)(&self.callee, x)
}
}``````

Since `Lazy` is going to own both the caller and callee, I think this would be better to avoid unnecessary closure copies?

#4

Thanks for the explanation, the cause of the error is clear now! I wasn’t realising that the reference was in fact instantiating a different type, I thought that some coertion was taking place.