I have a grasp of the Fn (capital-F) traits: Fn
, FnMut
, FnOnce
. I understand that they are traits and work like traits.
But wha
It is a function pointer type.
It refers only to a function, not a closure, since it contains just the address of the function not the captured environment a closure needs.
A Fn
trait (capital F) can refer either to a closure or a function.
fn is the type for a function pointer. See also here in the documentation: https://doc.rust-lang.org/std/primitive.fn.html
prog-fh's answer is essentially correct, but lacks some nuance. Rust has three kinds of function-like types:
Function items are what you get when you create a function by using fn foo() {...}
. It's also the type of the constructor of a tuple-like struct or enum variant. Function items are zero-sized (they contain no data), and every non-generic function has a unique, unnameable function item type. In error messages, the compiler displays these "Voldemort types" as something like fn() -> () {foo}
(with the name of the function in {}
).
Closures are values similar to function items, but closures may contain data: copies of or references to whatever variables they capture from their environment. As you already know, you create a closure by using closure syntax (|args| expression
). Like function items, closures have unique, unnameable types (rendered by the compiler something like [closure@src/main.rs:4:11: 4:23]
).
Function pointers are what you're asking about: the types that look like fn() -> ()
. Function pointers cannot contain data, but they are not zero-sized; as their name suggests, they are pointers. A function pointer may point either to a function item, or to a closure that captures nothing, but it cannot be null.
Function items and closures are automatically coerced to the relevant function pointer type when possible, so that's why let f: fn(i32) = |_| ();
works: because the closure captures nothing, it can be coerced to a function pointer.
All three function-like types implement the relevant Fn
, FnMut
and FnOnce
traits (except that closures might not implement Fn
or FnMut
depending on what they capture). Function items and function pointers also implement Copy
, Clone
, Send
and Sync
(closures only implement these traits when all their contents do).
Performance-wise, function pointers are something of a compromise between generics and trait objects. They have to be dereferenced to be called, so calling a function pointer may be slower than calling a function item or closure directly, but still faster than calling a dyn Fn
trait object, which involves a vtable lookup in addition to the indirect call.