devela::code::util

Macro cfor

Source
macro_rules! cfor {
    ($var:pat_param in ($range:expr).step_by($step:expr) => $body:stmt) => { ... };
    ($var:pat_param in ($range:expr).rev().step_by($step:expr) => $body:stmt) => { ... };
    ($var:pat_param in ($range:expr).rev() => $body:stmt) => { ... };
    ($var:pat_param in ($range:expr).step_by($step:expr).rev() => $body:stmt) => { ... };
    ($var:pat_param in $range:expr => $body:stmt) => { ... };
}
Expand description

A for loop that is usable in compile-time contexts.

It aims to work exactly like a normal for loop over a standard exclusive range, eg. 0..10 or -5..5. Unfortunately it doesn’t support other types of ranges like ..10 or 2..=10. So generally just use it like a regular for loop.

.rev() and .step_by(x) are implemented via macros instead of the non-const iter trait, and makes the loop behave as expected.

§Examples

let mut a = 0;
cfor!(i in 0..5 => {
    a += i
});
assert!(a == 10)

This is equivalent to the following regular for loop, except it is usable in const context.

let mut a = 0;
for i in 0..5 {
    a += i
}
assert!(a == 10)

§Custom step size

A custom step size can be set:

let mut v = Vec::new();
cfor!(i in (0..5).step_by(2) => {
    v.push(i)
});
assert!(v == vec![0, 2, 4])

The loop behaves as if the function was called on the range, including requiring a usize, but it is implemented by a macro.

§Reversed

Iteration can be reversed:

let mut v = Vec::new();
cfor!(i in (0..5).rev() => {
    v.push(i)
});
assert!(v == vec![4, 3, 2, 1, 0])

The loop behaves as if the function was called on the range, but it is implemented by a macro.

§Reversed and custom step size

It is possible to combine rev and step_by, but each can only be appended once. So the following two examples are the only legal combinations.

// Reverse, then change step size
let mut v = Vec::new();
cfor!(i in (0..10).rev().step_by(4) => {
    v.push(i)
});
assert!(v == vec![9, 5, 1]);

// Change step size, then reverse
let mut v = Vec::new();
cfor!(i in (0..10).step_by(4).rev() => {
    v.push(i)
});
assert!(v == vec![8, 4, 0])

§Notes

You can use mutable and wildcard variables as the loop variable, and they act as expected.

// Mutable variable
let mut v = Vec::new();
cfor!(mut i in (0..4) => {
    i *= 2;
    v.push(i)
});
assert!(v == vec![0, 2, 4, 6]);

// Wildcard variable
let mut a = 0;
cfor!(_ in 0..5 =>
   a += 1
);
assert!(a == 5)

The body of the loop can be any statement. This means that the following is legal, even though it is not in a regular for loop.

let mut a = 0;
cfor!(_ in 0..5 => a += 1);

unsafe fn unsafe_function() {}
cfor!(_ in 0..5 => unsafe {
   unsafe_function()
});