Skip to main content

const_tools/
fold.rs

1/// Folds an array into a single value in const contexts.
2///
3/// # Examples
4///
5/// ```
6/// use const_tools::{fold, destructure};
7///
8/// const fn last<T, const N: usize>(value: [T; N]) -> Option<T> {
9///     fold!(value, None, |prev, item| {
10///         std::mem::forget(prev); // Can't drop in const context, for the sake of illustration
11///         Some(item)
12///     })
13/// }
14/// ```
15#[macro_export]
16macro_rules! fold {
17    ($($fold_args:tt)*) => {
18        $crate::__fold__parse!([$($fold_args)*] $crate::__fold__expand!(<>))
19    };
20}
21
22#[doc(hidden)]
23#[macro_export]
24macro_rules! __fold__parse {
25    // [args] cb
26    ([zip!($($iae:expr),* $(,)?), $init:expr, |$acc:pat_param, ($($iip:pat_param),* $(,)?)| $($body:tt)*] $($cb:tt)*) => {
27        $crate::__zip_left!(
28            [$(($iae, $iip, ))*]
29            [(ia0) (ia1) (ia2) (ia3) (ia4) (ia5) (ia6) (ia7) (ia8) (ia9) (ia10) (ia11)]
30            "unsupported number of inputs"
31            $($cb)*([$init] [$($body)*] [$acc] <>)
32        )
33    };
34    ([$iae:expr, $init:expr, |$acc:pat_param, $iip:pat_param| $($body:tt)*] $($cb:tt)*) => {
35        $crate::__call!($($cb)*([$init] [$($body)*] [$acc] [($iae, $iip, ia0)]))
36    };
37    ([$iae:expr, $init:expr, $fn:expr] $($cb:tt)*) => {
38        $crate::__call!($($cb)*([$init] [$fn(acc, ii)] [acc] [($iae, ii, ia0)]))
39    };
40}
41
42#[doc(hidden)]
43#[macro_export]
44macro_rules! __fold__expand {
45    // [init] [body] [acc] [(iae, iip, ia)]
46    ([$init:expr] [$body:expr] [$acc:pat_param] [$(($iae:expr, $iip:pat_param, $ia:ident))+]) => {{
47        $(
48            let $ia = ::core::mem::ManuallyDrop::new($iae);
49            let $ia = $crate::__manually_drop_inner_ref(&$ia);
50        )*
51        let len = $crate::__same_len!($($ia),*);
52        let mut acc = $init;
53        let mut index = 0;
54        #[deny(unreachable_code)]
55        while index < len {
56            $(
57                let $iip = unsafe { ::core::ptr::read(&$ia[index]) };
58            )*
59            let $acc = acc;
60            acc = $body;
61            index += 1;
62        }
63        assert!(
64            index == len,
65            "break is not allowed because a value must be written into every array element"
66        );
67        acc
68    }};
69}