My goal is to make a macro that expands in such a way I can describe a tuple's initilization.
I made this toy example:
macro_rules! fill_tuple {
($_idx : expr) => {};
($idx : expr, $kind : ident) => {
$idx
};
($idx : expr, $head : ident, $($tail : ident),+) => {
fill_tuple!($idx, $head),
fill_tuple!($idx + 1usize, $($tail),+)
};
}
fn main()
{
(
fill_tuple!(0usize, A,B,C)
)
}
The idea here is, there are 3 parameters, so after expanding we should get (0, 1, 2). This is just an example, at the end I will actually use the A,B,C in a more sophisticated way to initialise the tuple.
My current hurdle is that the comma on the last block of the macro is not accepted by rust. I get the error:
Compiling playground v0.0.1 (/playground)
error: macro expansion ignores token `,` and any following
--> src/main.rs:7:33
|
7 | fill_tuple!($idx, $head),
| ^
...
15 | fill_tuple!(0usize, A,B,C)
| --------------------------- help: you might be missing a semicolon here: `;`
| |
| caused by the macro expansion here
|
= note: the usage of `fill_tuple!` is likely invalid in expression context
How do i get around this? I do absolutely need that comma, otherwise the whole generated code is wrong.
You can recursively go through the macro arguments and keep a list of your resulting numbers.
macro_rules! fill_tuple {
// Base cases
(@step 0, (),) => {
()
};
(@step $i:expr, ($($result:expr),*), $head:expr) => {
($($result,)* foo($i, $head),)
};
// Recursive case
(@step $i:expr, ($($result:expr),*), $head:expr, $($tail:expr),+) => {
fill_tuple!(@step $i + 1, ($($result,)* foo($i, $head)), $($tail),+)
};
// Public interface
($($e:expr),*) => {
fill_tuple!(@step 0, (), $($e),*)
};
}
And if you don't need the index, it's much more simple:
macro_rules! fill_tuple {
($($e:expr),*) => {
($(bar($e),)*)
}
}
There's some subtle details regarding single-element tuples because you have to put the value in parentheses and have a comma immediately after the value--for example, (7,)
instead of (7)
. So in the first macro, we need the comma after foo()
on the line ($($result,)* foo($i, $head),)
. Similarly, in the second macro we need ($(bar($e),)*)
instead of ($(bar($e)),*)
so that there's a comma after each value rather than each value except for the last.