const_for/
lib.rs

1//! # An ergonomic for loop for const contexts
2//! 
3//! [![GitHub](https://img.shields.io/badge/GitHub-black?logo=github)](https://github.com/JENebel/const_for)
4//! [![crates.io](https://img.shields.io/crates/v/const_for?logo=rust&logoColor=b7410e)](http://crates.io/crates/const_for)
5//! [![Docs](https://img.shields.io/docsrs/const_for/latest?logo=Docs.rs)](https://docs.rs/const_for/latest)
6//!
7//! Regular for loops are not allowed in const contexts, because it relies on iterators, which are not available in const.\
8//! This is rather annoying when writing const functions, as you need to write custom for loops using 'loop' or 'while'.
9//!
10//! This crate provides an ergonomic macro implementation of a for loop over a range, that is usable in const and `no_std` contexts.\
11//! 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.\
12//! 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.
13//! This is necessary, as normally they depend on non-const iterators. But they can be used here with identical syntax.
14//!
15//! The main restriction is that the macro only supports standard, exclusive, ranges, eg. 0..10 and -5..5, but not ..5 or 0..=10.
16//! 
17//! ```
18//! # use const_for::*;
19//! let mut a = 0;
20//! const_for!(i in 0..5 => {
21//!     a += i
22//! });
23//! assert!(a == 10)
24//! ```
25//! 
26//! This is equivalent to the following regular for loop, except it is usable in const context.
27//! 
28//! ```
29//! # use const_for::*;
30//! let mut a = 0;
31//! for i in 0..5 {
32//!     a += i
33//! }
34//! assert!(a == 10)
35//! ```
36//! 
37//! ## Custom step size
38//! 
39//! A custom step size can be set:
40//! 
41//! ```
42//! # use const_for::*;
43//! let mut v = Vec::new();
44//! const_for!(i in (0..5).step_by(2) => {
45//!     v.push(i)
46//! });
47//! assert!(v == vec![0, 2, 4])
48//! ```
49//! 
50//! The loop behaves as if the function was called on the range, but it is implemented by a macro.\
51//! It is equivalent to the following non-const loop:
52//! 
53//! ```
54//! # use const_for::*;
55//! let mut v = Vec::new();
56//! for i in (0..5).step_by(2) {
57//!     v.push(i)
58//! }
59//! assert!(v == vec![0, 2, 4])
60//! ```
61//! 
62//! ## Reversed
63//! 
64//! Iteration can be reversed:
65//! 
66//! ```
67//! # use const_for::*;
68//! let mut v = Vec::new();
69//! const_for!(i in (0..5).rev() => {
70//!     v.push(i)
71//! });
72//! assert!(v == vec![4, 3, 2, 1, 0])
73//! ```
74//! 
75//! The loop behaves as if the function was called on the range, but it is implemented by a macro.\
76//! It is equivalent to the following non-const loop:
77//! 
78//! ```
79//! # use const_for::*;
80//! let mut v = Vec::new();
81//! for i in (0..5).rev() {
82//!     v.push(i)
83//! }
84//! assert!(v == vec![4, 3, 2, 1, 0])
85//! ```
86//! 
87//! ## Reversed and custom step size
88//! 
89//! 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.
90//! 
91//! ```
92//! # use const_for::*;
93//! // Reverse, then change step size
94//! let mut v = Vec::new();
95//! const_for!(i in (0..10).rev().step_by(4) => {
96//!     v.push(i)
97//! });
98//! assert!(v == vec![9, 5, 1]);
99//! 
100//! // Change step size, then reverse
101//! let mut v = Vec::new();
102//! const_for!(i in (0..10).step_by(4).rev() => {
103//!     v.push(i)
104//! });
105//! assert!(v == vec![8, 4, 0])
106//! ```
107//! 
108//! ## Notes
109//! 
110//! You can use mutable and wildcard variables as the loop variable, and they act as expected.
111//! 
112//! ```
113//! // Mutable variable
114//! # use const_for::*;
115//! let mut v = Vec::new();
116//! const_for!(mut i in (0..4) => {
117//!     i *= 2;
118//!     v.push(i)
119//! });
120//! assert!(v == vec![0, 2, 4, 6]);
121//! 
122//! // Wildcard variable
123//! let mut a = 0;
124//! const_for!(_ in 0..5 => 
125//!    a += 1
126//! );
127//! assert!(a == 5)
128//! ```
129//! 
130//! 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.
131//! 
132//! ```
133//! # use const_for::*;
134//! let mut a = 0;
135//! const_for!(_ in 0..5 => a += 1);
136//! 
137//! unsafe fn unsafe_function() {}
138//! const_for!(_ in 0..5 => unsafe {
139//!    unsafe_function()
140//! });
141//! ```
142//!
143//! If the beginning of the range plus the step overflows the integer behaviour is undefined.
144//! 
145//! ### Real world example
146//! 
147//! Here is an example of how this crate helped make some actual code much nicer and readable.
148//! 
149//! The code was taken (and edited a bit for clarity) from the [Cadabra](https://github.com/JENebel/Cadabra/) chess engine.
150//! 
151//! Before:
152//! 
153//! ```
154//! const fn gen_white_pawn_attacks() -> [u64; 64] {
155//!     let mut masks = [0; 64];
156//!     
157//!     let mut rank: u8 = 0;
158//!     while rank < 8 {
159//!         let mut file: u8 = 0;
160//!         while file < 8 {
161//!             let index = (rank*8+file) as usize;
162//!             if file != 7 { masks[index] |= (1 << index) >> 7 as u64 }
163//!             if file != 0 { masks[index] |= (1 << index) >> 9 as u64 }
164//! 
165//!             file += 1;
166//!         }
167//!         rank += 1;
168//!     }
169//! 
170//!     masks
171//! }
172//! ```
173//! 
174//! After:
175//! 
176//! ```
177//! # use const_for::*;
178//! const fn gen_white_pawn_attacks() -> [u64; 64] {
179//!     let mut masks = [0; 64];
180//!     
181//!     const_for!(rank in 0..8 => {
182//!         const_for!(file in 0..8 => {
183//!             let index = (rank*8+file) as usize;
184//!             if file != 7 { masks[index] |= (1 << index) >> 7 as u64 }
185//!             if file != 0 { masks[index] |= (1 << index) >> 9 as u64 }
186//!         })
187//!     });
188//! 
189//!     masks
190//! }
191//! ```
192
193#![no_std]
194mod const_for;
195pub use const_for::*;