Having read #[derive] sometimes uses incorrect bounds · Issue #26925 · rust-lang/rust · GitHub, I agree that in general getting proper type bounds for #[derive(...)] is a can of worms. There's one special case I'm thinking about posting an issue for, though: PhantomData<T>. This type is already treated "specially"; I think that given its strong guarantee that T plays no role in any usage of the type things like this
use std::marker::PhantomData;
#[derive(Default)]
struct S<T>(PhantomData<T>);
should produce an implementation of Default with no bounds on T like this
The macros right now don't look at the field types at all.
The only things the std derives do is bound the list of generic type parameters.
(That's intentional, because that way changing the field types can't change the bounds. Though it's often not what people want, in more complicated situations.)
I wonder to what extent we could get the trait system to solve this? What if #[derive(Default)] on a struct had an implicit bound of FieldType: Default for every field? Then, this would have a bound of PhantomData<T>: Default, which in turn doesn't actually need T: Default. Similarly, Option<T>: Default doesn't need T: Default. But [T; 4]: Default would require T: Default.
(And even in cases where it's not directly breaking, it's potentially scary. Weakening the requirements might be logically incorrect, and that might be really really bad if there's unsafe code that's only sound today because of the current bounds.)
EventuallyTM I want to write an RFC that would permit arbitrary tokens to be provided as a parameter to derives. The built-in macros could then do #[derive(Default(where T: Default, Option<U>: Default))] and similar. It would also work for deriving const impls via #[derive(Default(const))]. No reason they couldn't be combined (comma-separated), either.