Thank you very much for the detailed explanation!
I am not sure whether I understand fully how the described pattern solves the match
case. I'll try to give a small description of the more concrete problem I try to solve.
We have a programming language with blocks, some blocks have parameters, others not. Let's take the following trivial example:
enum Block {
Init,
Button(ButtonInner),
...
}
We want to do something to all blocks, in my cases, it is generating a Flatbuffers serialization, using schemas matching the Rust block structure. What we want is something like this (syntax simplified):
fn to_fb(&self, builder: &mut FlatBufferBuilder) -> SomeFbType {
match self {
Block::Init => fb::Init::create(builder, &(InitArgs {})),
Block::Button(inner) => inner.to_fb(builder),
...
}
}
I am currently having a macro of the following type (the >
is the "guard" in my current weird syntax):
match_block_type_to_fb!{
self, builder,
Init => InitArgs,
> Button,
}
In case there is no inner
, there are two arguments because of constraints from Flatbuffers (I could probably have used the paste crate to avoid the second one but that is another topic), and where there is an inner
, we delegate to it. The macro ends-up being implemented like this:
macro_rules! match_block_type_to_fb {
( $self:ident, $builder:ident, $( $($block0:ident => $fb_arg_ty:ident)? $(> $block1:ident)? ),* ) => {
match $self {
$(
$(
Block::$block0 => fb::$block0::create($builder, &$fb_arg_ty {}),
)?
$(
Block::$block1(inner) => inner.to_fb($builder),
)?
)*
}
}
}
Inside the match I need to have rules with =>
, but in case of inner
, I need it on both sides. So I did not find a way to have a cleaner approach, am I missing something?
Ideally, it would be great to be able to write the macro like that:
macro_rules! match_block_type_to_fb {
( $self:ident, $builder:ident, $( $block $( => $fb_arg_ty:ident)? ),* ) => {
match $self {
$(
$(
Block::$block => fb::$block::create($builder, &$fb_arg_ty {}),
)else(
Block::$block(inner) => inner.to_fb($builder),
)?
)*
}
}
}