I have this code:
macro_rules! count {
() => { 1 };
}
#[derive(Debug)]
struct MyStruct<T> {
field_list: [T; count!()],
}
The compiler gives this error:
error: `derive` cannot be used on items with type macros
--> src/main.rs:7:21
|
7 | field_list: [T; count!()],
| ^^^^^^^^
Is there any way to use #[derive]
on a type containing an array where the length is specified by a macro?
Quoting my answer from the Github issue:
It is intentional (here is the historical record), but there is a possibility the situation could be improved in the future, and at least the error message should be rewritten to explain why it refuses to compile.
The underlying issue is that
#[derive]
macros need to "forward" their trait requirements to all the fields of the struct. ForMyStruct
to beDebug
, the type offield
must also beDebug
. Consider this one:#[derive(Debug)] struct MyStruct<T: FromStr> { field: T }
We need to generate
impl<T: FromStr> Debug for MyStruct<T> where T: Debug { ... }
(you'll see why I pickedFromStr
in a second). However in this case:#[derive(Debug)] struct MyStruct<T> { field: T::Err }
Here the field is an associated type, so the generated code actually needs to be
impl<T: FromStr> Debug for MyStruct<T> where T::Err: Debug { ... }
.The derive macros actually scan the field types to see whether they need to bound
T
or an associated type. But if you use a type macro, this breaks. The code generation can't see through the macro, so it doesn't know what bounds to generate.When this was discovered we couldn't decide whether to let the type macro be expanded eagerly (seems like you could get into a loop or ordering issues), just copy the macro into the
where
clause (derives normally don't do this because it could expand to a private type, causing type errors in generated code), or something else, so we punted and made it an error.
The problem can't really be fixed while obeying the "policies" of deriving: (1) it generates the bounds for you, and (2) it only generates code that compiles. But since custom derive is stable, there are crates you can use, like derivative, that sidestep the problem by letting you rewrite the bound:
#[derive(Derivative)]
#[derivative(Debug)]
struct MyStruct<T> {
#[derivative(Debug(bound="T: ::std::fmt::Debug"))]
field_list: [T; count!()],
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With