The purpose of this post is mostly: can we push macro_rules to do this ? Consider the following problem:
We have an enum.
Each arm of the enum is a constant.
We want to auto derive enum <-> usize
Now, the only part I am stuck in the macro_rules! -- within the repetition, I need a way to say "give me a list of incrementing numbers" almost like an .iter().enumerate()
Again, I am aware this can be solved via procedural macros. I am just curious if we can do it via macro_rules!
it's partially doable for the example code you posted.
this part is fairly straight forward to do, you just make the macro recursive invoke itself to construct tokens like (A, (1)) (B, ((1)+1)) (C, (((1)+1)+1)) etc
this part is impossible, macro_rules cannot generate tokens for the literal integer value 1, 2, etc. you can, however, implement it with a if else chain.
Wow, you guys are fast ... meanwhile I put together this implementation of the approach suggested by @nerditation. (not complete, not necessarily the best):
macro_rules! enumerate {
{$cont:ident! $data:tt $n:tt} => { $cont! $data; };
{$cont:ident! ($($data:tt)*) ($($n:tt)*) $x:tt $($xs:tt)*} => { enumerate!{$cont! ($($data)* (($($n)*+1) $x)) ($($n)*+1) $($xs)*} };
}
macro_rules! to_enum {
{ $name:ident $ty:ty; $((($index:expr) $variant:ident))* } => {
fn $name(value: i32) -> Option<$ty> {
#[allow(unused_parens)]
$(if value == $index { return Some(<$ty>::$variant) })*
None
}
}
}
enum Test{A, B, C}
enumerate!(to_enum!(to_enum_test Test;) (0) A B C );
// Expands to to_enum!(to_enum_test Test; ((0) A) ((0+1) B) ((0+1+1) C) )
The last one expands to:
fn to_enum_test(value: i32) -> Option<Test> {
#[allow(unused_parens)]
if value == 0 + 1 { return Some(<Test>::A) }
if value == 0 + 1 + 1 { return Some(<Test>::B) }
if value == 0 + 1 + 1 + 1 { return Some(<Test>::C) }
None
}
Thanks everyone; I will go procedural-macro route. I was just curious if (1) it was a limitation of my understanding of macro_rules! or (2) a limitation of macro_rules! itself. Thanks.
Writing a general macro is a hard job, because of Rust's flexible but not-easily-parsed syntax.
In your case where an enum has each single ident as its variants, if Copy trait is viable, here's a simple solution: Rust Playground
the usage of const is clever, as the input tokens are all unit enum variants, you don't need to generate hygienic identifiers for the constants. good job.