It doesn't seem like the compiler is enforcing type requirements on derive macros properly. For example, this does not compile, but seems like it should because Test derives Debug which necessitates that T is Debug (playground):
error[E0277]: `T` doesn't implement `std::fmt::Debug`
--> src/lib.rs:14:26
|
14 | println!("{:?}", self);
| ^^^^ `T` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug`
Whereas, this does compile, and I wouldn't think it should (playground):
#[derive(Debug)]
struct Test<T> {
a: T
}
struct NoDebugType {}
fn main() {
let t = Test::<NoDebugType>{ a: NoDebugType{} };
// println!("{:?}", t);
}
If you uncomment the println! line, then it will fail with:
error[E0277]: `NoDebugType` doesn't implement `std::fmt::Debug`
--> src/main.rs:12:22
|
12 | println!("{:?}", t);
| ^ `NoDebugType` cannot be formatted using `{:?}`
Is this a bug, or the way it's supposed to work? It's the first one of these that's tripping me up. I'm trying to build a trait for Result and want to print self however it tells me it the two types don't necessarily implement Debug even though Result derives Debug which would necessitate the type parameters being Debug. Thoughts? Thanks!
Ah, so the derive macro is adding an additional type constraint on the generic T that it too must be Debug. So it's legal to create an instance of Test without T: Debug, but the implementation only works when T implements Debug.
Not super-intuitive to me, but it makes more sense with the derive macro desugared... thanks!
Derive macros can't modify the declarations they are attached to. They can only generate additional code.
Furthermore, not allowing a type to be created unless type bounds are met is a great anti-pattern – you violate the "you only pay for what you use" principle. It's even recommended somewhere in the Book or in the Rust API Guidelines that generic types should only apply trait bounds inside impl blocks, not in the declaration itself.