Как я могу использовать # [производное] для типа, содержащего массив, длина которого указана макросом? - PullRequest
0 голосов
/ 11 мая 2018

У меня есть этот код:

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

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

Компилятор выдает эту ошибку:

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

Можно ли использовать #[derive] для типа, содержащего массив, длина которого указана макросом?

Ответы [ 2 ]

0 голосов
/ 14 мая 2018

Цитирую мой ответ на вопрос о Github:

Это сделано намеренно (вот историческая запись ), но есть вероятность, что ситуация может быть улучшена в будущем.и, по крайней мере, сообщение об ошибке следует переписать, чтобы объяснить, почему оно отказывается от компиляции.

Основная проблема заключается в том, что #[derive] макросы должны "перенаправить" свои требования к свойствам во все поля структуры.Чтобы MyStruct было Debug, тип field также должен быть Debug.Рассмотрим это:

#[derive(Debug)] struct MyStruct<T: FromStr> {
    field: T
}

Нам нужно сгенерировать impl<T: FromStr> Debug for MyStruct<T> where T: Debug { ... } (вы поймете, почему я выбрал FromStr в секунду).Однако в этом случае:

#[derive(Debug)] struct MyStruct<T> {
    field: T::Err
}

Здесь поле является ассоциированным типом, поэтому сгенерированный код действительно должен быть 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!()],
}
0 голосов
/ 11 мая 2018

Решением может быть использование двух макросов, одного макроса, который определяет структуру, и другого макроса, который определяет размер.

macro_rules! foo {
    ($x:expr) => {
        #[derive(Debug)]
        struct MyStruct<T> {
            field_list: [T; $x],
        }
    };
}

macro_rules! bar {
    () => {
        foo!(1);
    };
}

bar!();

Он работает на неуниверсальныхвведите, как вы сказали. Может быть, это ошибка компилятора?Должен ли я открыть проблему на Github?

Возможно, это не ошибка, а ограничение макросов.Однако вы можете открыть вопрос, чтобы попросить его улучшить.

...