So, let's say you have Foo<T>, but you wanna make it so that no two instances are interchangeable. You want each instantiation to be unique, so that you cannot even swap two variables defined on two separate lines. Not just a runtime error, but handled at the very type level. Well, dear reader, do I have a thing for you!
No two function types are the same
So, you know how functions and closures each have their own unique type, right? Unnameable, unique even despite identical names and signatures, and with exactly one instance (the function/closure itself). This is what allows Rust to perform powerful optimizations with things like map<F>() - in simple terms, each invocation of such a function is entirely unique, meaning that, for instance, the code can be inlined and optimized on a case-by-case basis.
let a = Some(42u8); // Option<u8>
let b = Some(42i8); // Option<i8>
let c = Some(|_x: u8| {}); // Option<closure 1>
let d = Some(|| {}); // Option<closure 2>
let e = Some(|| {}); // Option<closure 3>
// All of these are different types, even d and e!As you can see, this also applies to generic types - instantiating a type with a closure (or a function - but not a fn pointer!) creates a unique type per line. This is a simplification - the compiler doesn't actually care about line numbers - but it's effectively the same in this case.
The demo
Anyway, this led me to a little toy example, where every instantiation of a struct produces a unique type. These are not interchangeable, and consequently also not fully nameable (you must use Foo<_> or somehow refer to it indirectly).
Here's a Playground link, which demonstrates some other weirdness around doing stuff this way. Try building it to see the error messages; I also included some details in code comments.
My favorite thing is just how confusing the error message is. The compiler team is doing an excellent job making error messages detailed, clear, and even going out of their way to handle common gotchas, so this is in no way the fault of the error message - it's just funny in this deliberate edge case.
error[E0308]: mismatched types
--> src/lib.rs:32:9
|
14 | make(|| {})
| --
| |
| the expected closure
| the found closure
...
32 | a = b; // [ERR]
| ^ expected closure, found a different closure
|
= note: expected struct `Foo<{closure@src/lib.rs:14:18: 14:20}>`
found struct `Foo<{closure@src/lib.rs:14:18: 14:20}>`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait objectWhen two types differ by a function or closure type, Rust does its absolute best to clarify where the two unnameable function types come from. However, since the closure comes from a macro invocation, Rust ends up highlighting the same location in code, and even generates two identical pseudonames for the closures, both referring to the same line.
Luckily, the second note message comes to the rescue, and does its best to rectify the confusing situation. It explains that no two closures (or functions), however similar they may be, ever have the same type. It's a reminder that even if two type names print as identical strings, they might not be the same!