6 releases
0.1.5 | Sep 7, 2024 |
---|---|
0.1.4 | Jan 22, 2024 |
0.1.3 | Nov 4, 2023 |
0.1.2 | Mar 19, 2023 |
#85 in Build Utils
4,584 downloads per month
Used in 14 crates
(4 directly)
14KB
58 lines
An ergonomic for loop for const contexts
Regular for loops are not allowed in const contexts, because it relies on iterators, which are not available in const.
This is rather annoying when writing const functions, as you need to write custom for loops using 'loop' or 'while'.
This crate provides an ergonomic macro implementation of a for loop over a range, that is usable in const and no_std
contexts.
The aim is to imitate a regular for loop as closely as possible. It handles break and continue correctly, and the variable is immutable in the body.
To make the for loop as versatile as possible, it comes with macro variants to handle .rev() and step_by(x), which imitates the respective function calls.
This is necessary, as normally they depend on non-const iterators. But they can be used here with identical syntax.
The main restriction is that the macro only supports standard, exclusive, ranges, eg. 0..10 and -5..5, but not ..5 or 0..=10.
let mut a = 0;
const_for!(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();
const_for!(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, but it is implemented by a macro.
It is equivalent to the following non-const loop:
let mut v = Vec::new();
for i in (0..5).step_by(2) {
v.push(i)
}
assert!(v == vec![0, 2, 4])
Reversed
Iteration can be reversed:
let mut v = Vec::new();
const_for!(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.
It is equivalent to the following non-const loop:
let mut v = Vec::new();
for i in (0..5).rev() {
v.push(i)
}
assert!(v == vec![4, 3, 2, 1, 0])
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();
const_for!(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();
const_for!(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();
const_for!(mut i in (0..4) => {
i *= 2;
v.push(i)
});
assert!(v == vec![0, 2, 4, 6]);
// Wildcard variable
let mut a = 0;
const_for!(_ 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;
const_for!(_ in 0..5 => a += 1);
unsafe fn unsafe_function() {}
const_for!(_ in 0..5 => unsafe {
unsafe_function()
});
If the beginning of the range plus the step overflows the integer behaviour is undefined.
Real world example
Here is an example of how this crate helped make some actual code much nicer and readable.
The code was taken (and edited a bit for clarity) from the Cadabra chess engine.
Before:
const fn gen_white_pawn_attacks() -> [u64; 64] {
let mut masks = [0; 64];
let mut rank: u8 = 0;
while rank < 8 {
let mut file: u8 = 0;
while file < 8 {
let index = (rank*8+file) as usize;
if file != 7 { masks[index] |= (1 << index) >> 7 as u64 }
if file != 0 { masks[index] |= (1 << index) >> 9 as u64 }
file += 1;
}
rank += 1;
}
masks
}
After:
const fn gen_white_pawn_attacks() -> [u64; 64] {
let mut masks = [0; 64];
const_for!(rank in 0..8 => {
const_for!(file in 0..8 => {
let index = (rank*8+file) as usize;
if file != 7 { masks[index] |= (1 << index) >> 7 as u64 }
if file != 0 { masks[index] |= (1 << index) >> 9 as u64 }
})
});
masks
}
License: MIT