Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Rust, what is `fn() -> ()`?

I have a grasp of the Fn (capital-F) traits: Fn, FnMut, FnOnce. I understand that they are traits and work like traits.

But what about fn (lowercase-f)? It gets a different coloring in editors, which tells me it's not a trait. It can also be used in some places where the others can't (and vice-versa), though it seems to behave similarly in other cases. I couldn't find anything explaining it directly in the docs.

like image 938
brundolf Avatar asked Oct 10 '20 20:10

brundolf


2 Answers

Rust has three kinds of function-like types:

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

  2. 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]).

  3. 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. However, in real code there are many variables that confound naive analysis; if the difference in performance is important to you, you should measure it instead of guessing which is faster.

References

  • What's the practical difference between fn item and fn pointer?
  • Why design a language with unique anonymous types?
  • How do I make a struct for FFI that contains a nullable function pointer?
  • Why does passing a closure to function which accepts a function pointer not work?
like image 142
trent Avatar answered Oct 12 '22 03:10

trent


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.

like image 3
prog-fh Avatar answered Oct 12 '22 01:10

prog-fh