I have code like the following:
enum Either<L, R> {
Left(L),
Right(R)
}
enum SomeMessageType {
// ...
}
fn do_something<T>() {
let data: Vec<Either<T, SomeMessageType>> = ...;
// ...
}
I want to be able to iterate over the contents of data
without explicitly specifying the Either
types, because having to specify the Either
everywhere makes the API ugly and annoying to work with. The generic type T
in do_something
will always be an enum variant, I just don't know if it's possible to express that in Rust's generic types. Ideally, I'd like to be able to write something like:
fn do_something<...>() {
let data = ...;
matching_iterator!(data, {
SomeMessageType::... => ...
T::SomeVariant => ...
});
}
I have tried writing a macro like this already:
#[macro_export]
macro_rules! check_mail {
( $data:expr, { $( $pattern:pat => $handler:block )+ } ) => {
{
use $crate::build_pattern;
for data in $data.iter() {
if let $crate::build_pattern!($( $pattern )+) = message {
$( $handler )+
}
}
}
}
};
}
#[macro_export]
macro_rules! build_pattern {
( $pattern:pat ) => {
Either::Right($pattern)
};
( $pattern:pat ) => {
Either::Left($pattern)
};
}
but obviously this code won't compile, much less run. My intuition says I should put a differentiator of some sort at the start of each pattern, to make it easier to write the macro, but I haven't been able to get that to work. Each attempt generates the match arm code wrong, with all the matches at the start and then all the handler blocks at the end, and I'm not sure why.
You'd have to have some way to differentiate Left
and Right
patterns I used ;
here.
Also you can't match on variants of a generic cause it might be any type and might not even have them.
fn do_something() {
use Either::*;
let data = vec![Right(T::SomeVariant), Left(SomeMessageType::Good)];
matching_iterator!(data,
SomeMessageType::Good => {}
;
T::SomeVariant => {}
T::SomeOtherVariant => {}
);
}
#[macro_export]
macro_rules! matching_iterator {
( $data:expr, $($lpattern:pat => $lhandler:block)+; $($rpattern:pat => $rhandler:block)+ ) => {
{
for data in $data.iter() {
match data {
$(Either::Left($lpattern) => $lhandler)+
$(Either::Right($rpattern) => $rhandler)+
}
}
}
};
}