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}