I'm trying to remove code duplication in this Rust code using a macro:
enum AnyError { Error(Error), ParseIntError(ParseIntError), }
impl From<Error> for AnyError {
fn from(err: Error) -> AnyError { AnyError::Error(err) }
}
impl From<ParseIntError> for AnyError {
fn from(err: ParseIntError) -> AnyError { AnyError::ParseIntError(err) }
}
This is the macro code I'm trying to get working, which I believe should generate the above:
enum AnyError {
Error(Error),
ParseIntError(ParseIntError),
}
macro_rules! any_error {
($n: ident) => ();
($n: ident, $x:ident, $($y:ident),*) => {
impl From<$x> for $n {
fn from(err: $x) -> $n {
$n::$x(err)
}
}
any_error!($n, $($y),*);
};
}
any_error!(AnyError, Error, ParseIntError);
This is the error I'm getting from the compiler:
error: unexpected end of macro invocation
--> src/main.rs:17:28
|
9 | macro_rules! any_error {
| ---------------------- when calling this macro
...
17 | any_error!($n, $($y),*);
| ^ missing tokens in macro arguments
I've tried a bunch of different variants of this. If I remove the recursive call to any_error!
then it successfully generates one of the From
impls, so that part seems fine. Does anyone know what's wrong, or what the compiler error actually means?
The problem is that your macro handles zero variants, and two or more variants, but fails when there is exactly one:
any_error!(AnyError, Error); // None of the cases handle this!
A possible fix is to move the ,
into the $y
matcher, so that in the exactly one case, the macro doesn't expect a trailing comma:
macro_rules! any_error {
($n: ident) => ();
($n: ident, $x:ident $(, $y:ident)*) => {
impl From<$x> for $n {
fn from(err: $x) -> $n {
$n::$x(err)
}
}
any_error!($n $(, $y)*);
};
}
Alternatively, you can put the impl
inside the $()*
block and skip the recursion altogether:
macro_rules! any_error {
($n: ident) => ();
($n: ident, $($x:ident),*) => {
$(
impl From<$x> for $n {
fn from(err: $x) -> $n {
$n::$x(err)
}
}
)*
};
}