Search code examples
rustcallbackfunction-pointers

What's the practical difference between fn item and fn pointer?


fn func(_: i64) -> bool {
    true
}

fn func_of_func(callback: &fn(i64) -> bool, arg: i64) -> bool {
    (*callback)(arg)
}

fn main() {
    let is_positive = &func;
    println!("{}", func_of_func(is_positive, 8));
    println!("{}", func_of_func(is_positive, 8));
}

This doesn't compile:

error[E0308]: mismatched types
 --> src/main.rs:9:33
  |
9 |     println!("{}", func_of_func(is_positive, 8));
  |                                 ^^^^^^^^^^^ expected fn pointer, found fn item
  |
  = note: expected reference `&fn(i64) -> bool`
             found reference `&fn(i64) -> bool {func}`

Why does this error occur while I have passed a pointer, not fn? I want to know the practical difference between using fn and pointer to fn.


Solution

  • fn(i64) -> bool is already a function pointer, so &fn(i64) -> bool is a reference to a function pointer. Since function pointers are Copy, you should never have any reason to write this.

    If you're writing a function that takes something function-like as an argument, you should usually use generics (or impl Fn, as in Mike Graham's answer, which means the same thing):

    fn func_of_func<F: FnOnce(i64) -> bool>(callback: F, arg: i64) -> bool {
        callback(arg)
    }
    

    This means that when you call func_of_func with a function item such as func, callback will be compiled to a direct function call instead of a function pointer, which is easier for the compiler to optimize.

    If the function cannot be made generic (perhaps because it's a member of an object safe trait), you should usually use trait objects instead, which allows the caller to pass a closure:

    fn func_of_func(callback: &dyn Fn(i64) -> bool, arg: i64) -> bool { ... }
    fn func_of_func(callback: &mut dyn FnMut(i64) -> bool, arg: i64) -> bool { ... }
    // using `FnOnce` requires boxing
    fn func_of_func(callback: Box<dyn FnOnce(i64) -> bool>, arg: i64) -> bool { ... }
    

    Function pointers should only be used when the function definitely cannot capture anything. They are mostly useful for FFI with C, and as type parameters to PhantomData in generic structs.

    References