fn item 和 fn pointer 之间的实际区别是什么?

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));
}

这不编译:

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}`

为什么我传递的是指针而不是 fn 时会出现此错误?我想知道使用 fn 和指向 fn 的指针之间的实际区别。

你应该可以用

解决这个问题
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));
}

或者更直接地不添加间接级别

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));
}

更常见的是使用 Fn 特征,它的好处是允许闭包和函数

fn func(x: i64) -> bool {
    true
}
    
fn func_of_func(callback: impl FnOnce(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));
}

fn(i64) -> boolalready a function pointer,所以&fn(i64) -> bool是对函数指针的引用。由于函数指针是 Copy,你永远不应该写这个。

如果你正在编写一个函数,它接受一些东西 function-like 作为参数,你通常应该使用泛型(或 impl Fn,如 ,这意味着同样的事情):

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

这意味着当你用func这样的函数项调用func_of_func时,callback将被编译为直接函数调用而不是函数指针,这样更容易供编译器优化。

如果函数不能被通用化(可能是因为它是对象安全特征的成员),您通常应该使用特征对象来代替,这允许调用者传递闭包:

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 { ... }

只有当函数绝对不能捕获任何东西时才应该使用函数指针。它们主要用于使用 C 的 FFI,并作为泛型结构中 PhantomData 的类型参数。

参考资料