wish there was a convenient way in rust to check if a peeked value from iterator matches a pattern, and if so take the value and pattern match with it without having to do a redundant unwrap for a .next() when the value is definitely already there because it’s been peeked, without having to do a redundant match on the taken value, and without having to write two similar but different patterns for the reference from .peek() and for the value from .next()
i wrote a very bad very naughty very unsafe macro to do it with a new struct with the same layout and a helper method and std::mem::transmute like so:
very naughty macro
macro_rules! match_next {
(<$I:ty> $iter:expr, {$($pat:pat => $exp:expr),*}) => ({
use crate::match_next::MatchNext;
(unsafe {
std::mem::transmute::<&mut std::iter::Peekable<$I>, &mut MatchNext<$I>>(&mut $iter)
}).next_or_reinsert(|v| match v {
$($pat => Ok((|| $exp)())),*,
_ => Err(v)
})
});
}i’m a little bit proud of myself for thinking to wall off $exp in (|| ...)() to prevent returning to the outer lambda but i wish i could just outright forbid return in $exp altogether (imo it being accepted in this context at all is downright unhygienic but i could just be straight up wrong) and there’s probably some other edge cases i’m not thinking of. i considered manually matching _ patterns but decided it was pointless because if match_next!(..., _ => v, ...) produces T instead of Option<T> because the match is infallible, that’s just equivalent to match iter.next().unwrap() {...}, and if not, the higher caller-defined _ clause will just shadow the fallback one.
i feel so fucked up and evil (positive?) just looking at this macro. i haven’t even used it yet
made a slightly less horrible version but forgot to post it
slightly less horrible macro
macro_rules! match_next {
(<$I:ty> $iter:expr; $($pat:pat $(if $guard:expr)? => $exp:expr),*) => ({
struct MatchNext<I>
where I: Iterator
{
iter: I,
peeked: Option<Option<<I>::Item>>
}
let p = unsafe {
std::mem::transmute::<&mut std::iter::Peekable<$I>, &mut MatchNext<$I>>($iter)
};
let peek_or_next = p.peeked.take().unwrap_or_else(|| p.iter.next());
if let Some(item) = peek_or_next {
match item {
$($pat $(if $guard)? => Some($exp), )*
_ => {
p.peeked = Some(Some(item));
None
}
}
} else {
None
}
})
}