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}