如何在包含长度由宏指定的数组的类型上使用#[derive]?

Zz *_*Tux 5 macros rust

我有这个代码:

macro_rules! count {
    () => { 1 };
}

#[derive(Debug)]
struct MyStruct<T> {
    field_list: [T; count!()],
}
Run Code Online (Sandbox Code Playgroud)

编译器给出了这个错误:

error: `derive` cannot be used on items with type macros
 --> src/main.rs:7:21
  |
7 |     field_list: [T; count!()],
  |                     ^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

有没有办法#[derive]在包含数组的类型上使用,其中长度由宏指定?

dur*_*a42 6

引用我在Github问题上的回答:

这是故意的(这是历史记录),但是将来情况可能会得到改善,至少应该重写错误消息以解释为什么拒绝编译。

根本问题是#[derive]宏需要将其特征要求“转发”到结构的所有字段。为了 MyStruct成为Debug,其类型field也必须是Debug。考虑一下这个:

#[derive(Debug)] struct MyStruct<T: FromStr> {
    field: T
}
Run Code Online (Sandbox Code Playgroud)

我们需要生成(你很快impl<T: FromStr> Debug for MyStruct<T> where T: Debug { ... }就会明白我为什么选择 )。FromStr然而在这种情况下:

#[derive(Debug)] struct MyStruct<T> {
    field: T::Err
}
Run Code Online (Sandbox Code Playgroud)

这里的字段是关联类型,因此生成的代码实际上需要是impl<T: FromStr> Debug for MyStruct<T> where T::Err: Debug { ... }.

派生宏实际上扫描字段类型以查看它们是否需要绑定T或关联类型。但如果你使用类型宏,这就破坏了。代码生成无法看透宏,因此它不知道要生成什么边界。

当发现这一点时,我们无法决定是否让类型宏急切地扩展(似乎您可能会陷入循环或排序问题),只需将宏复制到子句中where(派生通常不会这样做,因为它可能会扩展为私有类型,导致生成的代码中出现类型错误),或者其他什么,所以我们下注并使其成为错误。

在遵守派生“策略”的同时,问题无法真正得到解决:(1)它为您生成边界,(2)它只生成可编译的代码。但由于自定义衍生是稳定的,因此您可以使用一些板条箱,例如衍生品,它可以通过让您重写边界来回避问题:

#[derive(Derivative)]
#[derivative(Debug)]
struct MyStruct<T> {
    #[derivative(Debug(bound="T: ::std::fmt::Debug"))]
    field_list: [T; count!()],
}
Run Code Online (Sandbox Code Playgroud)