This video went into a deep dive of how Rust models the types of function in the language.
Function Types
All functions have a unique type, even if they have the same signature.
Why is this important?
It allows the compiler to optimize your code when working with higher-ordered functions, because it knows the exact definition of your function and can “inline” it in the compilation process.
In C++
In C++, function types are represented only by their signature. So when working with higher-ordered functions, the compiler has to create bytecode that works with a general function with that signature (even if you only call it with a concrete function implementation).
This is more inefficient, as it has to generate more bytecode, and has to perform an indirect call to something, somewhere at runtime—the CPU cannot optimize for this like by performing instruction pre-fetching.
What people writing C++ code have to do instead, is to wrap the function call in a lambda, which behaves similar to Rust’s function type interpretation. So a user has to sacrifice readability just to get performance.
Mental Model of Rust Function Types
Rust represents a function type by the “shape” of byte instructions of that function.
Rust Compiler Is Aware of Function “Synonyms”
Since Rust monomorphizes generic functions during compilation, it could theoretically blow up compile time and binary sizes since every function is considered unique.
If two functions, with different names, have exactly the same or similar implementations, their bytecodes will be the same and the compiler recognizes those function types as “synonyms” of each other. This allows it to monomorphize once during compilation.