#![feature(slice_patterns)] /* Destructure an iterator using the syntax of slice_patterns. * Wraps the match body in `Some` if there was a match; returns * `None` otherwise. * Contrary to slice_patterns, this allows moving out * of the iterator. * A variable length pattern (`x..`) is only allowed as the last * pattern, unless the iterator is double-ended. * * Example: * ``` * let vec = vec![Some(1), Some(2), None]; * * destructure_iter!(vec.into_iter(); * [Some(x), y.., z] => { * // x: usize * // y: impl Iterator> * // z: Option * } * ) * ``` * */ #[macro_export] macro_rules! destructure_iter { // Variable length pattern (@match 0, $iter:expr, ($body:expr), $x:ident.., $($rest:tt)*) => { $crate::destructure_iter!(@match 1, $iter, ({ let $x = $iter; $body }), $($rest)*) }; // Variable length pattern without a binder (@match 0, $iter:expr, ($body:expr), .., $($rest:tt)*) => { $crate::destructure_iter!(@match 1, $iter, ($body), $($rest)*) }; // Single item pattern (@match 0, $iter:expr, ($body:expr), $x:pat, $($rest:tt)*) => { if let Some($x) = $iter.next() { $crate::destructure_iter!(@match 0, $iter, ($body), $($rest)*) } else { None } }; // Single item pattern after a variable length one: declare reversed and take from the end (@match 1, $iter:expr, ($body:expr), $x:pat, $($rest:tt)*) => { $crate::destructure_iter!(@match 1, $iter, ( if let Some($x) = $iter.next_back() { $body } else { None } ), $($rest)*) }; // Check no elements remain (@match 0, $iter:expr, ($body:expr) $(,)*) => { if $iter.next().is_some() { None } else { $body } }; // After a variable length pattern, everything has already been consumed (@match 1, $iter:expr, ($body:expr) $(,)*) => { $body }; ($iter:expr; [$($args:tt)*] => $body:expr) => { { #[allow(unused_mut)] let mut iter = $iter; $crate::destructure_iter!(@match 0, iter, (Some($body)), $($args)*,) } }; } /* Pattern-match on a vec using the syntax of slice_patterns. * Wraps the match body in `Some` if there was a match; returns * `None` otherwise. * A variable length pattern (`x..`) returns an iterator. * * Example: * ``` * let vec = vec![Some(1), Some(2), None]; * * match_vec!(vec; * [Some(x), y.., z] => { * // x: usize * // y: impl Iterator> * // z: Option * } * [x, Some(0)] => { * // x: Option * }, * [..] => { } * ) * ``` * */ #[macro_export] macro_rules! match_vec { ($arg:expr; $( [$($args:tt)*] => $body:expr ),* $(,)*) => { { let vec = $arg; // Match as references to decide which branch to take // I think `match_default_bindings` should make this always work but // there may be some patterns this doesn't capture. #[allow(unused_variables, unreachable_patterns)] match vec.as_slice() { $( [$($args)*] => { // Actually consume the values #[allow(unused_mut)] let mut iter = vec.into_iter(); $crate::destructure_iter!(iter; [$($args)*] => $body) } )* _ => None, } } }; } /* Pattern-match on an iterator using the syntax of slice_patterns. * Wraps the match body in `Some` if there was a match; returns * `None` otherwise. * * Example: * ``` * let vec = vec![Some(1), Some(2), None]; * * match_iter!(vec.into_iter(); * [Some(x), y.., z] => { * // x: usize * // y: impl Iterator> * // z: Option * } * [x, Some(0)] => { * // x: Option * }, * [..] => { * ) * ``` * */ #[macro_export] macro_rules! match_iter { ($arg:expr; $($args:tt)*) => { { let vec: Vec<_> = $arg.collect(); $crate::match_vec!(vec, $($args)*) } }; } #[test] fn test() { let test = |v: Vec>| { match_vec!(v.into_iter(); [Some(_x), None, None] => 4, [Some(_x), None] => 2, [None, Some(y)] => 1, [None, _y..] => 3, [_x.., Some(y), Some(z)] => y - z, [] => 0, [..] => -1, ) .unwrap() }; assert_eq!(test(vec![Some(0), None, None]), 4); assert_eq!(test(vec![Some(0), None]), 2); assert_eq!(test(vec![None, Some(0)]), 1); assert_eq!(test(vec![Some(1), Some(2), Some(5), Some(14)]), -9); assert_eq!(test(vec![None]), 3); assert_eq!(test(vec![]), 0); assert_eq!(test(vec![Some(0)]), -1); // Test move out of pattern struct Foo; let _: (Foo, Foo) = match_vec!(vec![Some(Foo), Some(Foo)].into_iter(); [Some(f1), Some(f2)] => (f1, f2), ) .unwrap(); }