Reputation: 377
fn a() {}
seems to satisfy a parsing rule that expected a fn
, then some other stuff. item
s should be able to be function definitions, right? So they should work, right?
macro_rules! multi_impl {
(for $base:ty :
$($t:ty {
$($i:item);*
}),+) =>
{
$(
impl $t for $base
{
$( $i )*
}
)+
}
}
trait A {
fn a();
}
trait B {
fn b();
}
struct S;
multi_impl! {
for S:
A {
fn a() {}
}, B {
fn b() {}
}
}
fn main() {
S::a();
S::b();
}
The error in question:
error: expected one of `const`, `default`, `extern`, `fn`, `pub`, `type`, `unsafe`, or `}`, found `fn a() { }`
--> <anon>:11:20
|
11 | $( $i )*
| ^^
Making it $( fn $i)*
only changes the error to complain about expecting an identifier after the fn
, which makes sense, but the initial error does not (at least to me).
Is there a difference to the parser about code that is in the source vs code that's placed into the source by a macro?
Upvotes: 0
Views: 216
Reputation: 59005
The problem isn't that a fn
isn't an item, it's that the body of an impl
does not contain items. It contains "impl items". What it's complaining about is you trying to put a square-shaped block into a round hole, not that the block is the wrong colour.
Yes, those are two different things. No, you can't capture "impl items" in a macro. No, you can't turn an item into an impl item. Because macros capture AST nodes, not the tokens. Well, methods can have a self
argument and regular function's don't. I don't know, presumably it seemed like a good idea at the time.
Putting the hypothetical back-and-forth aside, the solution in this case is to not bother trying to match anything in particular and just match anything.
macro_rules! multi_impl
{
(for $base:ty :
$($t:ty {
$($body:tt)*
}),+) =>
{
$(
impl $t for $base
{
$($body)*
}
)+
}
}
Upvotes: 3