init_with/
lib.rs

1//! A helper trait to initialize a data structure with custom code.
2//!
3//! This crate is meant to aid in initializing fixed arrays using something other than a `Default`
4//! implementation. For example, if you wanted to create an array of Vecs, you could create one
5//! with `Default` that made all the Vecs empty:
6//!
7//! ```rust
8//! let my_array = <[Vec<u32>; 3]>::default();
9//! assert_eq!(my_array, [[], [], []]);
10//! ```
11//!
12//! But if you wanted to start the arrays with some state, you either need to start with the empty
13//! arrays and fill from there, or drop into unsafe code to write in a partially-initialized array.
14//!
15//! ```rust
16//! let mut my_array = <[Vec<usize>; 3]>::default();
17//!
18//! for (idx, arr) in my_array.iter_mut().enumerate() {
19//!     for i in 0..(idx+1) {
20//!         arr.push(i);
21//!     }
22//! }
23//!
24//! assert_eq!(my_array, [vec![0], vec![0, 1], vec![0, 1, 2]]);
25//! ```
26//!
27//! With `InitWith`, the same array could be initialized like this:
28//!
29//! ```rust
30//! use init_with::InitWith;
31//!
32//! let my_array = {
33//!     let mut seed = Vec::new();
34//!     let mut next_val = 0;
35//!
36//!     <[Vec<u32>; 3]>::init_with(|| {
37//!         seed.push(next_val);
38//!         next_val += 1;
39//!         seed.clone()
40//!     })
41//! };
42//!
43//! assert_eq!(my_array, [vec![0], vec![0, 1], vec![0, 1, 2]]);
44//! ```
45//!
46//! Alternatively, `init_with_indices` can be used to more easily create array entries based on their index:
47//!
48//! ```rust
49//! use init_with::InitWith;
50//!
51//! let squares = <[usize; 5]>::init_with_indices(|i| i*i);
52//!
53//! assert_eq!(squares, [0,1,4,9,16]);
54//! ```
55//!
56//! This crate is built with `#![no_std]` and only uses libcore for its code, so it can be used
57//! from other `no_std` crates.
58
59#![no_std]
60
61/// A trait that allows you to create an instance of a type by using a given function to generate
62/// each element.
63pub trait InitWith<T> {
64    /// Create a new instance of this type using the given function to fill elements.
65    ///
66    /// # Examples
67    ///
68    /// Prefilling an array with a Vec, with no unsafe code:
69    ///
70    /// ```rust
71    /// use init_with::InitWith;
72    ///
73    /// let src = vec![1, 2, 3];
74    /// let dest: [i32; 3] = {
75    ///     let mut idx = 0;
76    ///
77    ///     <[i32; 3]>::init_with(|| {
78    ///         let val = src[idx];
79    ///         idx += 1;
80    ///         val
81    ///     })
82    /// };
83    ///
84    /// assert_eq!(src, dest);
85    /// ```
86    fn init_with<F>(init: F) -> Self
87    where
88        F: FnMut() -> T,
89        Self: Sized;
90
91    /// Create a new instance of this type to fill elements
92    /// by mapping the given function over the new array's indices.
93    ///
94    /// # Examples
95    ///
96    /// Prefilling an array of even numbers, with no unsafe code:
97    ///
98    /// ```rust
99    /// use init_with::InitWith;
100    ///
101    /// let src = vec![0, 2, 4];
102    /// let dest = <[i32; 3]>::init_with_indices(|x| 2*x as i32);
103    ///
104    ///
105    /// assert_eq!(src, dest);
106    /// ```
107    fn init_with_indices<F>(init: F) -> Self
108    where
109        F: FnMut(usize) -> T,
110        Self: Sized;
111}
112
113macro_rules! array_init {
114    {$n:expr, $init:ident, $($stack:ident,)+} => {
115        impl<T> InitWith<T> for [T; $n] {
116            fn init_with<F>(mut $init: F) -> Self
117                where F: FnMut() -> T,
118                Self: Sized
119            {
120                [$init(), $($stack()),+]
121            }
122            fn init_with_indices<F>(mut $init: F) -> Self
123                where F: FnMut(usize) -> T
124            {
125                build_incrementing_list!([], 0, $init, $($stack),+)
126            }
127        }
128        array_init!{($n - 1), $($stack,)+}
129    };
130    {$n:expr, $init:ident,} => {
131        impl<T> InitWith<T> for [T; $n] {
132            fn init_with<F>(mut $init: F) -> Self
133                where F: FnMut() -> T,
134                Self: Sized
135            {
136                [$init()]
137            }
138            fn init_with_indices<F>(mut $init: F) -> Self
139                where F: FnMut(usize) -> T,
140                Self: Sized
141            {
142                [$init(0)]
143            }
144        }
145        array_init!{($n - 1)}
146    };
147    {$n:expr} => {
148        impl<T> InitWith<T> for [T; $n] {
149            fn init_with<F>(_init: F) -> Self
150                where F: FnMut() -> T,
151                Self: Sized
152            {
153                []
154            }
155            fn init_with_indices<F>(_: F) -> Self
156                where F: FnMut(usize) -> T,
157                Self: Sized
158            {
159                []
160            }
161        }
162    };
163}
164
165macro_rules! build_incrementing_list {
166	{[$($result:tt)*], $n:expr, $head:ident} => { 
167		[$($result)* $head($n),]
168	};
169	{[$($result:tt)*], $n:expr, $head:ident, $($stack:ident),+} => { 
170		build_incrementing_list!([$($result)* $head($n),], $n+1, $($stack),+)
171	};
172}
173
174array_init!{32, init, init, init, init, init, init, init, init, init, init, init, init, init, init, init, init, init, init, init, init, init, init, init, init, init, init, init, init, init, init, init, init,}
175
176#[cfg(test)]
177mod tests {
178    use super::InitWith;
179
180    #[test]
181    fn expected() {
182        let val = <[i32; 3]>::init_with(|| 4);
183        assert_eq!(val, [4, 4, 4]);
184    }
185
186    #[test]
187    fn expected_build_incrementing_list() {
188        let val = <[usize; 5]>::init_with_indices(|x| x);
189        assert_eq!(val, [0, 1, 2, 3, 4]);
190    }
191}