array_fu/lib.rs
1//!
2//! This crate defines [`array!`] and other macros that can construct arrays.
3//! Use simple syntax, make it more complex as requirements change.
4//!
5//! [`array!`] macro constructs arrays by repeating expression execution, possibly with enumeration bound to provided pattern.
6//!
7//! # Examples
8//!
9//! ```
10//! # use array_fu::array;
11//! # use rand::random;
12//! let values: [f32; 2] = array![random(); 2];
13//! ```
14//!
15//! This also means that expression type may not be `Copy` or event `Clone`.
16//!
17//! ```
18//! # use array_fu::array;
19//! # use std::sync::Mutex;
20//! let values = array![Mutex::new(1); 2];
21//! ```
22//!
23//! See more examples in the [`array!`] macro documentation.
24//!
25//!
26//! [`collect_array!`] macro constructs arrays by repeating expression execution with elements from iterators bound to provided patterns.
27//!
28//! # Examples
29//!
30//! ```
31//! # use array_fu::collect_array;
32//! let opt = collect_array![x in 1.., y in 2.. => x + y; where x * y > 10; 3];
33//!
34//! assert_eq!(opt, Some([7, 9, 11]));
35//! ```
36//!
37//! ```
38//! # use array_fu::collect_array;
39//! let values = collect_array![(x, y) in [(1, 2), (3, 4), (5, 6)] => x + y; 3];
40//!
41//! assert_eq!(values, Some([3, 7, 11]));
42//! ```
43//!
44//! See more examples in the [`collect_array!`] macro documentation.
45//!
46#![no_std]
47
48use core::{
49 mem::{self, MaybeUninit},
50 ptr,
51};
52
53#[doc(hidden)]
54pub type Usize = usize;
55
56#[doc(hidden)]
57pub use core::{iter::IntoIterator, num::Wrapping, ops::Not};
58
59#[doc(hidden)]
60pub struct DontBreakFromElementExpressionWithoutLabel;
61
62#[doc(hidden)]
63pub fn type_name_of_val<T: ?Sized>(_val: &T) -> &'static str {
64 ::core::any::type_name::<T>()
65}
66
67#[doc(hidden)]
68pub struct PartiallyInitArray<T, const N: usize> {
69 array: [MaybeUninit<T>; N],
70 init: usize,
71}
72
73impl<T, const N: usize> PartiallyInitArray<T, N> {
74 pub fn uninit() -> Self {
75 PartiallyInitArray {
76 // Could be written as `array![MaybeUninit::uninit(); N]`
77 array: unsafe {
78 // SAFETY: An uninitialized `[MaybeUninit<_>; N]` is valid.
79 MaybeUninit::uninit().assume_init()
80 },
81 init: 0,
82 }
83 }
84
85 /// # Safety
86 ///
87 /// Must be called at most `N` times.
88 /// Or equivalently, until `is_init` returns false.
89 #[inline]
90 pub unsafe fn write(&mut self, value: T) {
91 debug_assert!(self.init < N);
92 self.array[self.init].write(value);
93 self.init += 1;
94 }
95
96 #[inline]
97 pub fn is_init(&self) -> bool {
98 self.init == N
99 }
100
101 /// # Safety
102 ///
103 /// Must be called after `write` was called exactly `N` times.
104 /// Or equivalently, when `is_init` returns true.
105 #[inline]
106 pub unsafe fn assume_init(self) -> [T; N] {
107 debug_assert_eq!(self.init, N);
108 let array = {
109 // SAFETY: Fully initialized.
110 mem::transmute_copy::<[MaybeUninit<T>; N], [T; N]>(&self.array)
111 };
112 mem::forget(self);
113 array
114 }
115
116 #[inline]
117 pub fn try_init(self) -> Option<[T; N]> {
118 if self.init == N {
119 let array = unsafe {
120 // SAFETY: Fully initialized.
121 mem::transmute_copy::<[MaybeUninit<T>; N], [T; N]>(&self.array)
122 };
123 mem::forget(self);
124 Some(array)
125 } else {
126 None
127 }
128 }
129}
130
131impl<T, const N: usize> Drop for PartiallyInitArray<T, N> {
132 fn drop(&mut self) {
133 let slice = &mut self.array[..self.init];
134 unsafe { ptr::drop_in_place(slice as *mut [MaybeUninit<T>] as *mut [T]) }
135 }
136}
137
138#[doc(hidden)]
139#[macro_export]
140macro_rules! pattern_list {
141 ($ph:pat, $($pt:pat,)*) => {
142 $crate::pattern_list!($($pt,)* ; $ph )
143 };
144 ($ph:pat, $($pt:pat,)* ; $r:pat) => {
145 $crate::pattern_list!($($pt,)* ; ($r, $ph) )
146 };
147 (; $r:pat) => {
148 $r
149 };
150}
151
152/// Constructs arrays by repeating expression execution,
153/// possibly with enumeration bound to provided pattern.
154///
155/// # Syntax
156///
157/// On the basic level, arrays construction happens by repeating execution of provided expression multiple times.
158/// Note that the expression itself appears exactly once in expanded code.
159/// And length expression is executed in const context exactly once.
160///
161/// ```
162/// # use array_fu::array;
163/// let values = array![1; 2];
164///
165/// assert_eq!(values, [1, 1]);
166/// ```
167///
168/// Unlike built-in syntax `[$expr; $size]` `array!` runs expression `$size` times instead of copying result.
169/// This means that expression will exhibit its side effects for each array element,
170/// and value can change freely.
171///
172/// ```
173/// # use array_fu::array;
174/// # use rand::random;
175/// let values: [f32; 2] = array![random(); 2];
176/// ```
177///
178/// This also means that expression type may not be `Copy` or event `Clone`.
179///
180/// ```
181/// # use array_fu::array;
182/// # use std::sync::Mutex;
183/// let values = array![Mutex::new(1); 2];
184/// ```
185///
186/// ## Enumerate
187///
188/// `array!` macro supports enumerating while constructing array elements.
189///
190/// `array!($pat => $expr ; $n)` does the trick. That's it, simply add `$pat =>` before element expression.
191///
192/// `$pat` must be valid pattern. And it will be bound to numbers starting from 0.
193/// Bound value can be used in the element expression.
194///
195/// ```
196/// # use array_fu::array;
197/// let values = array![x => x + 1; 3];
198///
199/// assert_eq!(values, [1, 2, 3]);
200/// ```
201///
202/// ## Predicates
203///
204/// `array!` macro supports predicated that are evaluated before element expression for each constructed element.
205/// When predicate does not pass, element expression is not executed.
206/// Value bound to pattern will be updated before trying again.
207///
208/// ```
209/// # use array_fu::array;
210/// let values = array![x => x + 1; where x & 1 == 1; 3];
211///
212/// assert_eq!(values, [2, 4, 6]);
213/// ```
214///
215/// It is possible to make array expression infeasible.
216/// For example by providing predicate that never evaluates to true.
217///
218/// ```should_panic
219/// # use array_fu::array;
220///
221/// // predicate always evaluates to `false`
222/// // making it impossible to construct array of size 1 or greater.
223/// // This will lead to a panic with descriptive message.
224/// // `[u8; 1]` type forces enumerator to be `u8` allowing it to fail faster.
225/// let _: [u8; 1] = array![x => x; where false; 1];
226/// ```
227///
228/// ## Control flow
229///
230/// Element expressions and conditions are executed in the inner loop scope but in the outer function.
231/// This makes it possible to perform early return from macro invocation using `return` and `break` and `continue` statements.
232/// `continue` and `break` won't compile without a label. If label is provided, they will behave as expected.
233/// `return` would exit function where macro is called.
234/// If size of the array is `0`, element and condition expressions won't be executed even once
235/// and `return` statement won't exit the function.
236/// This behavior is different from `[return; 0]` which performs early return regardless.
237///
238/// ```
239/// # use array_fu::array;
240/// array![return; 1];
241/// ```
242///
243/// ```compile_fail
244/// # use array_fu::array;
245/// array![break; 1];
246/// ```
247///
248/// ```compile_fail
249/// # use array_fu::array;
250/// array![continue; 1];
251/// ```
252///
253/// ```
254/// # use array_fu::array;
255/// 'a: loop { array![break 'a; 1]; };
256/// ```
257///
258/// ```
259/// # use array_fu::array;
260/// 'a: for _ in 0..3 { array![continue 'a; 1]; };
261/// ```
262///
263/// ## List
264///
265/// For consistency with built-in syntax, arrays may be constructed with a list of expressions.
266///
267/// ```
268/// # use array_fu::array;
269/// let values = array![1, 2, 3];
270///
271/// assert_eq!(values, [1, 2, 3]);
272/// ```
273#[macro_export]
274macro_rules! array {
275 ($($e:expr),* $(,)?) => { [$($e,)*] };
276
277 ($e:expr; $n:expr) => {{
278 $crate::array!( _ => $e ; $n )
279 }};
280
281 ($p:pat => $e:expr $( ; where $( $cond:expr ),+ )? ; $n:expr) => {{
282 #[allow(unused_mut)]
283 let mut array = $crate::PartiallyInitArray::<_, $n>::uninit();
284
285 let mut i = $crate::Wrapping(0);
286 loop {
287 let value = i.0;
288 i += 1;
289
290 if i.0 == 0 {
291 panic!("Failed to initialize array using whole '{}' space", $crate::type_name_of_val(&i.0));
292 }
293
294 if array.is_init() {
295 // This is the only way ouf of the loop without leaving outer scope.
296 break;
297 }
298
299 match value {
300 $p => {
301 #[allow(unreachable_code)]
302 {
303 $($(
304 #[allow(unused_variables)]
305 #[warn(unreachable_code)]
306 let cond = $cond;
307
308 if <bool as $crate::Not>::not(cond) { continue; }
309 )+)?
310
311 #[allow(unused_variables)]
312 let elem;
313
314 #[allow(unused_variables)]
315 let dont_continue_in_element_expression_without_label;
316
317 loop {
318 #[allow(unused)]
319 {
320 dont_continue_in_element_expression_without_label = ();
321 }
322
323 #[allow(unused_variables)]
324 #[warn(unreachable_code)]
325 let value = $e;
326
327 elem = value;
328
329 break $crate::DontBreakFromElementExpressionWithoutLabel;
330 };
331
332 unsafe {
333 array.write(elem);
334 }
335 }
336 }
337 #[allow(unreachable_patterns)]
338 _ => continue,
339 }
340 }
341
342 unsafe {
343 // SAFETY: `is_init` returned true.
344 array.assume_init()
345 }
346 }};
347}
348
349/// Constructs arrays by repeating expression
350/// with elements from iterators bound to provided patterns.
351///
352/// Creating arrays from iterators is really handy.
353/// But it comes at price - there could be not enough values in the iterator to fill the array.
354///
355/// Therefore this macro returns `Option`.
356/// `Some` array is returned if there were enough values.
357/// Otherwise `None` is returned.
358///
359/// ```
360/// # use array_fu::collect_array;
361/// let opt = collect_array![1..; 3];
362///
363/// assert_eq!(opt, Some([1, 2, 3]));
364/// ```
365///
366/// `None` is returned otherwise.
367///
368/// ```
369/// # use array_fu::collect_array;
370/// let opt = collect_array![1..3; 3];
371///
372/// assert_eq!(opt, None, "There's only two elements in 1..3");
373/// ```
374///
375/// Similarly to `array!` macro, `collect_array` can be given a pattern to bind iterator elements
376/// and expression to produce array elements.
377///
378/// ```
379/// # use array_fu::collect_array;
380/// let opt = collect_array![x in 1.. => x / 2; 3];
381///
382/// assert_eq!(opt, Some([0, 1, 1]));
383/// ```
384///
385/// But why stop there? Multiple iterators can be collected into an array!
386///
387/// ```
388/// # use array_fu::collect_array;
389/// let opt = collect_array![x in 1.., y in 2.. => x + y; 3];
390///
391/// assert_eq!(opt, Some([3, 5, 7]));
392/// ```
393///
394/// Surely it also supports predicates.
395/// When predicate evaluates to `false`, next items are taken from all iterators.
396///
397/// ```
398/// # use array_fu::collect_array;
399/// let opt = collect_array![x in 1.., y in 2.. => x + y; where x * y > 10; 3];
400///
401/// assert_eq!(opt, Some([7, 9, 11]));
402/// ```
403///
404/// Patterns support destructuring.
405///
406/// ```
407/// # use array_fu::collect_array;
408/// let values = collect_array![(x, y) in [(1, 2), (3, 4), (5, 6)] => x + y; 3];
409///
410/// assert_eq!(values, Some([3, 7, 11]));
411/// ```
412///
413/// And patterns don't have to be irrefutable.
414///
415/// ```
416/// # use array_fu::collect_array;
417/// let values = collect_array![(1, y) in [(1, 2), (3, 4), (1, 6)] => y; 2];
418///
419/// assert_eq!(values, Some([2, 6]));
420/// ```
421#[macro_export]
422macro_rules! collect_array {
423 ($it:expr; $n:expr) => {
424 $crate::collect_array!(e in $it => e ; $n)
425 };
426
427 ($e:expr; $ph:pat in $ih:expr $( , $pt:pat in $it:expr )* $(; where $($cond:expr),+ )? ; $n:expr) => {{
428 #[allow(unused_mut)]
429 let mut array = $crate::PartiallyInitArray::<_, $n>::uninit();
430
431 let iter = $crate::IntoIterator::into_iter($ih);
432 $( let iter = iter.zip($it); )*
433 let mut iter = iter;
434
435 loop {
436 if array.is_init() {
437 break;
438 }
439
440 match iter.next() {
441 None => break,
442 Some($crate::pattern_list!($ph, $( $pt, )*)) => {
443 #[allow(unreachable_code)]
444 {
445 $($(
446 #[allow(unused_variables)]
447 #[warn(unreachable_code)]
448 let cond = $cond;
449
450 if <bool as $crate::Not>::not(cond) { continue; }
451 )+)?
452
453 #[allow(unused_variables)]
454 let elem;
455
456 #[allow(unused_variables)]
457 let dont_continue_in_element_expression_without_label;
458
459 loop {
460 #[allow(unused)]
461 {
462 dont_continue_in_element_expression_without_label = ();
463 }
464
465 #[allow(unused_variables)]
466 #[warn(unreachable_code)]
467 let value = $e;
468
469 elem = value;
470
471 break $crate::DontBreakFromElementExpressionWithoutLabel;
472 };
473
474 unsafe {
475 array.write(elem);
476 }
477 }
478 }
479 #[allow(unreachable_patterns)]
480 _ => continue,
481 }
482 }
483
484 array.try_init()
485 }};
486
487 ($( $p:pat in $i:expr ),+ => $e:expr $(; where $($cond:expr),+ )? ; $n:expr) => {
488 $crate::collect_array!($e; $($p in $i),+ $( ; where $($cond),+ )? ; $n)
489 };
490}
491
492#[test]
493fn test_expression_repeat() {
494 let mut i = 0;
495 assert_eq!(array!({ i+=1; i }; 2), [1, 2]);
496}
497
498#[test]
499fn test_comprehension_repeat() {
500 assert_eq!(array!(x => x * 2; 3), [0, 2, 4]);
501 assert_eq!(array!(x => x * 2; where x & 1 == 1; 3), [2, 6, 10]);
502}
503
504#[test]
505fn test_comprehension_iter() {
506 assert_eq!(
507 collect_array!(x * 2; x in 1..3; 3),
508 None,
509 "There's not enough elements in iterator"
510 );
511 assert_eq!(
512 collect_array!(x * 2; x in 1..; 3),
513 Some([2, 4, 6]),
514 "1*2, 2*2, 3*2"
515 );
516 assert_eq!(
517 collect_array!(x * y; x in 1.., y in (1..3).cycle(); where x > 3, y == 1; 3),
518 Some([5, 7, 9]),
519 "x = 1,2,3,4,5,6,7,8,9
520 y = 1,2,1,2,1,2,1,2,1
521 r = x,x,x,x,5,x,7,x,9"
522 );
523
524 assert_eq!(
525 collect_array!(x in 0.. => x * 2; where x & 1 == 1; 3),
526 Some(array!(x => x * 2; where x & 1 == 1; 3)),
527 );
528
529 assert_eq!(
530 collect_array!(x in 0.., _y in 1.., _z in 2.., _w in 3..5 => x; where x & 1 == 1; 3),
531 None,
532 );
533}
534
535#[test]
536fn test_bail() {
537 array!(return; 2);
538 panic!();
539}
540
541#[test]
542fn test_bail_condition() {
543 array!(_ => 0; where return; 1);
544 panic!();
545}
546
547#[test]
548fn test_bail_iter() {
549 collect_array!(_ in 1.. => 0; where return; 1);
550 panic!();
551}
552
553#[test]
554#[should_panic]
555fn test_bail_panic() {
556 array!(return; 0);
557 panic!();
558}
559
560#[test]
561#[should_panic]
562fn test_bail_condition_panic() {
563 array!(_ => 0; where return; 0);
564 panic!();
565}