Reputation: 154
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.
Upvotes: 0
Views: 201
Reputation: 8689
I may be misunderstanding, but you can probably hide that with one or more callbacks ?
i.e.:
fn do_something<T, F, H>(f: F, h: H)
where F: Fn(T) -> (),
H: Fn(SomeMessageType) -> ()
{
let data = ...;
data.for_each(|either| {
match either {
Either::Left(t) => f(t),
Either::Right(r) => h(r),
}
})
}
Which you can then call with whichever handler you want for both types:
do_something(
|t| { match t { MyEnum::Some => ... }},
|msg| { println!("I received this message: {}", msg); }
);
Would that solve your problem, as the Either
implementation is hidden in do_something
Upvotes: 0
Reputation: 27549
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)+
}
}
}
};
}
Upvotes: 1