In the definition of foo
, you have this: $field_ty:ty
. This is what's breaking it.
Think about it like this: the input to a macro (what is being matched on) is a sequence of simple tokens. These simple tokens support literal matches (i.e. matching two sequences of arbitrary tokens to make sure they're the same).
When you perform a capture like $field_ty:ty
, the macro processor goes over to the compiler's parser and asks "hey, can you turn this chunk of random tokens into a Type tree for me?". Which it does. When you then substitute that captured $field_ty
into the output of the macro, the macro processor does not turn the captured Type tree back into random tokens, it substitutes the syntax tree node directly.
The problem this creates is that this magic "not really a token anymore" token does not support literal matching. You can re-capture it, but you can't "deconstruct" it into its constituent tokens.
Basically, once you capture Handle<u8>
as a ty
token, you cannot match against the Handle
part ever again.
Unfortunately, there's no easy way to really fix this. When I've needed to do something like this previously, I've had to resort to a recursive macro that matches each field one at a time. That way, you can do the "is this a Handle
" check before falling back to ty
matching.
Edit: okay, here's a playpen with a working macro. Or just read it here:
struct Handle<T>(std::marker::PhantomData<T>);
macro_rules! foo {
(
$vis:vis struct $name:ident {
$($fields:tt)*
}
) => {
foo! {
@parse
{ $vis struct $name },
{ },
$($fields)*
}
};
// `Handle<T>` field.
(
@parse
$bundle:tt,
{ $($fields:tt)* },
$field_name:ident : Handle<$field_ty:ty>,
$($tail:tt)*
) => {
foo! {
@parse
$bundle,
{
$($fields)*
$field_name: $field_ty,
},
$($tail)*
}
};
// Other field.
(
@parse
$bundle:tt,
{ $($fields:tt)* },
$field_name:ident : $field_ty:ty,
$($tail:tt)*
) => {
foo! {
@parse
$bundle,
{
$($fields)*
$field_name: $field_ty,
},
$($tail)*
}
};
// You can make `,`s optional by adding two more cases for the above where
// the field is the *last* thing in the input and doesn't have a comma.
// End of input.
(
@parse
{ $vis:vis struct $name:ident },
{ $($fields:tt)* },
$(,)*
) => {
$vis struct $name {
$($fields)*
}
};
}
foo! {
struct Foo {
foo: Handle<u8>,
bar: String,
}
}
That or write a proc macro. Depending on what you're doing, that might be simpler.