The output of macros have to be valid rust code, but the arguments don't. You can get around this limitation by passing the intermediate results as macro arguments, and only producing the match statement at the end:
macro_rules! match_index {
// Wrap the user-provided arguments in `@(...)`
($e:expr; $($rest:tt)*) => {
match_index!{@(0; $e; $($rest)*,)}
};
// Interpret one match arm
(@($idx:expr; $e:expr; $val:expr, $($rest:tt)*) $($arms:tt)*) => {
match_index!{
// Next index, match argument, remaining match results
@(1+$idx; $e; $($rest)*)
$($arms)*
x if x == $idx => $val,
}
};
// No more match arms, produce final output
(@($idx:expr; $e:expr; $(,)?) $($arms:tt)* ) => {
match $e {
$($arms)*
_ => unreachable!()
}
};
}
Minor nit @2e71828: when binding a caller-provided :expr to a local, use a match rather than a let, so as to mimic as much as possible the semantics of the hypothetical equivalent match_index() function call (get let … in { … } semantics):
macro_rules! match_index {
($e:expr; $($result:expr),*) => ({
let mut i = 0..;
match $e { e => {
$(if e == i.next().unwrap() { $result } else)*
{ unreachable!() }
}}
})
}
Yes, you are right, I also stumbled over this, but still, I want to know a match implementation for study.
I somehow figured out a solution just like you suggested, but it failed when the arms number goes larger than 2. I think it's a stupid problem, but I can't just figure out why.