Ответ 1
После ключевого слова struct
парсер ожидает дерево токенов идентификатора, за которым может следовать <
и больше; ty определенно не то, что он хочет (в качестве примера того, почему он не работает, (Trait + Send + 'static)
является допустимым типом, но struct (Trait + Send + 'static);
явно не имеет смысла).
Для поддержки дженериков вам нужно будет создать больше правил.
macro_rules! make_struct {
($name:ident) => {
struct $name;
};
($name:ident<$($t:ident: $constraint:ident),+>) => {
struct $name<$($t: $constraint),+>;
}
}
Как вы, несомненно, заметите, что он поддерживает все, что парсер будет принимать в этом месте, почти невозможно; macro_rules is not that clever. Тем не менее, этот один странный трюк (анализаторы статического кода ненавидят!), Который позволяет вам взять последовательность деревьев токенов и рассматривать его как обычное определение struct
. Он использует немного больше косвенных ссылок с macro_rules:
macro_rules! item {
($item:item) => ($item);
}
macro_rules! make_struct {
($name:ident) => {
struct $name;
};
($name:ident<$($tt:tt)*) => {
item!(struct $name<$($tt)*;);
};
}
Обратите внимание, что из-за чрезмерности, ident
в настоящее время не допускает повторение последовательности ($(…)
), чтобы немедленно следовать за ней, поэтому вы будете заклеивать, помещая некоторое дерево токенов между ident
и повторением, например. $name:ident, $($tt:tt)*
(давая AnotherStruct, <T: SomeTrait>
) или $name:ident<$(tt:tt)*
= > struct $name<$($tt)*;
. Ценность этого сводится к тому, что вы не можете полностью разделить его на отдельные типы, которые вам нужно будет сделать для таких вещей, как вставка маркеров PhantomData
.
Вам может быть полезно передать весь элемент struct
; он проходит как тип item
(так же, как a fn
, enum
, use
, trait
, & c.).