I'm trying to write a macro that generates a From
impl for an enum by wrapping the type in a certain variant.
I've come up with this:
macro_rules! variant_derive_from {
($enum:ty:$variant:ident($from:ty)) => {
impl From<$from> for $enum {
fn from(thing: $from) -> $enum { return $enum::$variant(thing) }
}
};
}
However, whenever I try to actually use this macro I get the following error:
error: expected expression, found `B`
| fn from(thing: $from) -> $enum { $enum::$variant(thing) }
| ^^^^
I couldn't figure out why this might be happening, so I ran a build with macro tracing on, and the macro apparently expands to the following (dummy types, of course):
impl From < A > for B { fn from ( thing : A ) -> B { B :: AVariant ( thing ) } }
When pasting this directly into the code, it compiles successfully. What gives??
Here's a full example of the error.
The most widely used form of macros in Rust is the declarative macro. These are also sometimes referred to as “macros by example,” “ macro_rules! macros,” or just plain “macros.” At their core, declarative macros allow you to write something similar to a Rust match expression.
Rust's macros are very good. They act like Lisp's macros, unlike Haskell's. The fact that Rust has type-classes (“traits”) and sum types (“enums”) and pattern matching is very attractive. Making orphan instances package-wide rather than module-wide is a very good decision.
macro_rules allows users to define syntax extension in a declarative way. We call such extensions "macros by example" or simply "macros". Each macro by example has a name, and one or more rules.
Rust provides a powerful macro system that allows metaprogramming. As you've seen in previous chapters, macros look like functions, except that their name ends with a bang ! , but instead of generating a function call, macros are expanded into source code that gets compiled with the rest of the program.
A ty
placeholder only works as type, not as a namespace. Using ident
instead should work just fine in your example.
ident
doesn't accept path
s ("foo::Bar
") though (and using path
also doesn't seem to work in this case); you should be able to workaround it by manually constructing paths like matching this: $enum:ident $(:: $enum_path:ident)*
(use it like this: $enum $(:: $enum_path)*
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With