#[doc = crate::doc_!(vendor: "const_for")]
#[macro_export]
#[cfg_attr(cargo_primary_package, doc(hidden))]
macro_rules! cfor {
($var:pat_param in ($range:expr).step_by($step:expr) => $body:stmt) => {
{
let _: usize = $step;
let mut __ite = $range.start;
let __end = $range.end;
let mut __is_first = true;
let __step = $step;
loop {
if !__is_first {
__ite += __step
}
__is_first = false;
let $var = __ite;
if __ite >= __end {
break
}
$body
}
}
};
($var:pat_param in ($range:expr).rev().step_by($step:expr) => $body:stmt) => {
{
let _: usize = $step;
let mut __ite = $range.end;
let __start = $range.start;
let mut __is_first = true;
let __step = $step;
loop {
if !__is_first {
if __step + __start >= __ite {
break
}
__ite -= __step
}
__is_first = false;
if __ite <= __start {
break
}
let $var = __ite - 1;
$body
}
}
};
($var:pat_param in ($range:expr).rev() => $body:stmt) => {
cfor!($var in ($range).rev().step_by(1) => $body)
};
($var:pat_param in ($range:expr).step_by($step:expr).rev() => $body:stmt) => {
cfor!($var in ($range.start..$range.end - ($range.end - $range.start - 1) % $step)
.rev().step_by($step) => $body)
};
($var:pat_param in $range:expr => $body:stmt) => {
cfor!($var in ($range).step_by(1) => $body)
};
}
#[doc(inline)]
pub use cfor;
#[cfg(all(test, feature = "alloc"))]
mod tests {
use super::cfor;
use crate::{Vec, vec_ as vec};
macro_rules! validate_loop {
(@impl $($loop:tt)*) => {
let mut c_values_hit = Vec::new();
cfor!(i in $($loop)* => {
c_values_hit.push(i);
});
let mut r_values_hit = Vec::new();
for i in $($loop)* {
r_values_hit.push(i);
};
assert!(c_values_hit == r_values_hit);
};
($step: expr, $($loop:tt)*) => {
validate_loop!(@impl ($($loop)*).step_by(1));
validate_loop!(@impl ($($loop)*).step_by(1).rev());
validate_loop!(@impl ($($loop)*).rev().step_by(1));
};
($($loop:tt)*) => {
validate_loop!(@impl $($loop)*);
validate_loop!(@impl ($($loop)*).rev());
validate_loop!(1, $($loop)*);
validate_loop!(2, $($loop)*);
validate_loop!(3, $($loop)*);
validate_loop!(8, $($loop)*);
validate_loop!(15, $($loop)*);
validate_loop!(17, $($loop)*);
validate_loop!(45, $($loop)*);
validate_loop!(150, $($loop)*);
};
}
#[test]
#[allow(unused_parens, reason = "(0..10)")]
fn equivalent_to_regular_for() {
validate_loop!(-10..10);
validate_loop!(0..10);
validate_loop!(-10..10);
validate_loop!((0..10));
validate_loop!(50..10);
validate_loop!(-15..-12);
validate_loop!(-14..0);
validate_loop!(-100..-50);
validate_loop!(-14..80);
validate_loop!(1..80);
}
#[test]
fn capture_range_at_beginning() {
let mut a = 113;
cfor!(i in 0..a-100 => {
a += i;
});
let mut b = 113;
for i in 0..b - 100 {
b += i;
}
assert_eq!(a, b);
let mut a = 0;
let mut step = 1;
cfor!(_ in (0..10).step_by(step) => {
a += step;
step += 1;
});
let mut b = 0;
let mut step = 1;
for _ in (0..10).step_by(step) {
b += step;
step += 1;
}
assert_eq!(a, b);
}
#[test]
const fn available_in_const() {
let mut a = 0;
cfor!(_ in 0..25 => {
a += 1
});
cfor!(_ in (0..25).rev() => {
a += 1
});
cfor!(_ in (0..100).step_by(2) =>
a += 1
);
cfor!(mut i in (0..3) => {
i += 1;
a += i
});
cfor!(_ in (0..7).rev() => {
a += 1
});
assert!(a == 25 + 25 + 50 + 6 + 7);
}
#[test]
fn no_underflow() {
cfor!(_ in (0u64..1).rev() => {});
let mut iterations: u64 = 0;
cfor!(_ in (i8::MIN..0).rev() => iterations += 1);
assert_eq!(iterations, 128);
}
#[test]
fn signed_can_go_negative() {
let mut actual = Vec::new();
cfor!(i in (-10..11).rev().step_by(5) => actual.push(i));
assert_eq!(actual, vec![10, 5, 0, -5, -10]);
}
}