Use PhantomData<fn() -> (T0, T1, ...)> everywhere
This was a trick I saw here: https://github.com/astral-sh/ruff/pull/10849#discussion_r1562158940
This has a lot of nice qualities for derive that can help us with #2068:
- Eq
- Copy
- Debug
- Serialize/Deserialize (with
#[serde(skip)]) - Send/Sync (in case we ever do any async)
If a lifetime is stored in the PhantomData, then use PhantomData<fn() -> &'a &'b ... (T0, T1, ...)>.
Where exactly will this help us? Think I'm too stupid to understand
No, this one's pretty arcane. I had no idea this trick existed until yesterday.
Basically, we currently require e.g. T: Debug in a lot of places because we hold PhantomData<T>, but this is actually an excessive restriction because T is never debug'd. Similarly with T: Copy, T: Eq, T: Serialize + Deserialize: we probably have lots of places where we can simplify the generic constraints because we never actually handle the type except in passing (e.g. in methods).
When deriving the Debug trait, the type parameter T is required to implement Debug as well. However, this requirement is imposed by the derive macro itself, not by the presence of PhantomData<T>. Therefore, changing PhantomData<T> to PhantomData<fn() -> T> will not alter this requirement. The derive macro will still enforce T: Debug regardless of which version of PhantomData is used.
So it would seem. This is very annoying, I thought I checked that... :confused: