• she or it or they

horn
@horn

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()


horn
@horn

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.


horn
@horn

i feel so fucked up and evil (positive?) just looking at this macro. i haven’t even used it yet


horn
@horn

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
        }
    })
}

You must log in to comment.

in reply to @horn's post: