const_array_init/
lib.rs

1#![no_std]
2
3//! # Macros used to initialize an array in const context using `closure` syntax or `const fn`.
4//! 
5//! All macros in this crate implemented using `macro_rules!` which is very **IDE-friendly** way.
6//!
7//! # Examples
8//!
9//! Using `const_arr!` macro:
10//! ```
11//! use const_array_init::const_arr;
12//! 
13//! const ARR1: [i32; 5] = const_arr!([i32; 5], |i| i as i32 + 1);
14//! assert_eq!(ARR1, [1, 2, 3, 4, 5]);
15//! 
16//! const fn to_i32_plus_one(n: usize) -> i32 {
17//!     n as i32 + 1
18//! }
19//! const ARR2: [i32; 5] = const_arr!([i32; 5], to_i32_plus_one);
20//! assert_eq!(ARR2, [1, 2, 3, 4, 5]);
21//! ```
22//! 
23//! Using `make_const_arr!` macro:
24//! ```
25//! use const_array_init::make_const_arr;
26//! 
27//! make_const_arr!(ARR1, [i32; 5], |i| i as i32 + 1);
28//! assert_eq!(ARR1, [1, 2, 3, 4, 5]);
29//! 
30//! const fn to_i32_plus_one(n: usize) -> i32 {
31//!     n as i32 + 1
32//! }
33//! 
34//! make_const_arr!(ARR2, [i32; 5], to_i32_plus_one);
35//! assert_eq!(ARR2, [1, 2, 3, 4, 5]);
36//! ```
37//! Advanced usage:
38//! - Note that `User` isn't `Copy`, yet still you can use it in `const` context with this macro.
39//! 
40//! ```
41//! use const_array_init::const_arr;
42//! 
43//! #[derive(Debug, PartialEq, Eq)]
44//! struct User { id: u32 }
45//! 
46//! const fn create_user_from_i(i: usize) -> User {
47//!     User { id: i as u32 }
48//! }
49//! 
50//! const USERS: [User; 1024] = const_arr!([User; 1024], create_user_from_i);
51//! 
52//! const USERS2: [User; 1024] = const_arr!([User; 1024], |i| User { id: i as u32 });
53//! ```
54
55/// ### Macro used to initialize arrays in constant context
56/// #### Supports both `closure` syntax and `const fn` initialization.
57/// 
58/// Usage:
59/// ```ignore
60/// const ARR: [TYPE; SIZE] = const_arr!([TYPE; SIZE], CONST_INIT_FN);
61/// ```
62/// 
63/// - `CONST_INIT_FN` is const function or const-like closure from 
64/// `array index`(`usize`) to `TYPE`
65/// 
66/// ### Examples:
67/// ```
68/// use const_array_init::const_arr;
69/// 
70/// const ARR1: [i32; 5] = const_arr!([i32; 5], |i| i as i32 + 1);
71/// assert_eq!(ARR1, [1, 2, 3, 4, 5]);
72/// 
73/// const fn to_i32_plus_one(n: usize) -> i32 {
74///     n as i32 + 1
75/// }
76/// 
77/// const ARR2: [i32; 5] = const_arr!([i32; 5], to_i32_plus_one);
78/// assert_eq!(ARR2, [1, 2, 3, 4, 5]);
79/// ```
80/// You have to specify array type in const context, even if compiler can infer it.
81/// 
82/// This is good `quick-fix` opportunity for your language server.
83/// 
84/// ```ignore
85/// const ARR = const_arr!([i32; 5], |i| i as i32);
86/// //    ^^^ help: provide a type for the constant: `: [i32; 5]`
87/// const ARR: [i32; 5] = const_arr!([i32; 5], |i| i as i32);
88/// ```
89/// 
90/// But if you don't want to specify type twice you can use 
91/// - `make_const_arr!(NAME, [TYPE; SIZE], INIT_FN)` macro.
92/// 
93/// ```
94/// use const_array_init::make_const_arr;
95/// 
96/// make_const_arr!(ARR, [i32; 5], |i| i as i32 + 1);
97/// 
98/// assert_eq!(ARR, [1, 2, 3, 4, 5]);
99/// ```
100/// 
101/// - See [`make_const_arr`]
102#[macro_export]
103#[rustfmt::skip]
104macro_rules! const_arr {
105    ([$TYPE:ty; $SIZE:literal], $func_name:ident) => {
106        {
107            // Create array of proper SIZE and initialize it with garbage data 
108            // using $func_name(0) call as if every value had index 0.
109            // 
110            // There is no way to create array without initializing it and
111            // we cannot initialize it with 0-s because it isn't always valid (e.g. references)
112            // and MaybeUninit is unsafe and unstable in const context.
113            const TEMP_ITEM: $TYPE = $func_name(0);
114            let mut arr: [$TYPE; $SIZE] = [TEMP_ITEM; $SIZE];
115
116            // Initialize array with proper data using $func_name(ind) call
117            let mut ind = 0;
118            while ind < $SIZE {
119                arr[ind] = $func_name(ind);
120                ind += 1;
121            }
122            arr
123        }
124    };
125    ([$TYPE:ty; $SIZE:literal], |$name:ident| $body:expr) => {
126        {
127            // Create array of proper SIZE and initialize it with garbage data 
128            // using $body with $name predefined to 0 as if every value had index 0.
129            // 
130            // There is no way to create array without initializing it and
131            // we cannot initialize it with 0-s because it isn't always valid (e.g. references)
132            // and MaybeUninit is is unsafe and unstable in const context.
133            #[allow(non_upper_case_globals)]
134            let mut arr: [$TYPE; $SIZE] = {
135                const $name: usize = 0;
136                const TEMP_ITEM: $TYPE = $body;
137                [TEMP_ITEM; $SIZE]
138            };
139
140            // Initialize array with proper data from closure's body
141            let mut $name = 0;
142            while $name < $SIZE {
143                arr[$name] = $body;
144                $name += 1;
145            }
146            arr
147        }
148    };
149    ([$TYPE:ty; $SIZE:literal], |_| $body:expr ) => {
150        {
151            const TEMP_ITEM: $TYPE = $body;
152            [TEMP_ITEM; $SIZE]
153        }
154    };
155    () => {compile_error!("Please specify array type TYPE: \n      const ARR: [TYPE; SIZE] = const_arr!([TYPE; SIZE], INIT_FN);\n e.g. const ARR: [i32;  10  ] = const_arr!([i32;  10  ], |i| i as i32);"); };
156    ([$type:ty; $size:literal]) => {compile_error!("Please specify init function INIT_FN: \n      const ARR: [TYPE; SIZE] = const_arr!([TYPE; SIZE], INIT_FN);\n e.g. const ARR: [i32;  10  ] = const_arr!([i32;  10  ], |i| i as i32);"); };
157    ([$type:ty; $size:literal], ) => {compile_error!("Please specify init function INIT_FN: \n      const ARR: [TYPE; SIZE] = const_arr!([TYPE; SIZE], INIT_FN);\n e.g. const ARR: [i32;  10  ] = const_arr!([i32;  10  ], |i| i as i32);"); };
158    ([$type:ty; $size:literal], ||) => {compile_error!("Init function has wrong format. It should be |i| i: \n      const ARR: [TYPE; SIZE] = const_arr!([TYPE; SIZE], INIT_FN);\n e.g. const ARR: [i32;  10  ] = const_arr!([i32;  10  ], |i| i as i32);"); };
159    ([$type:ty; $size:literal], || $_wha:tt) => {compile_error!("Init function has wrong format. It should be |i| i: \n      const ARR: [TYPE; SIZE] = const_arr!([TYPE; SIZE], INIT_FN);\n e.g. const ARR: [i32;  10  ] = const_arr!([i32;  10  ], |i| i as i32);"); };
160    ([$type:ty; $size:literal], $num:literal) => {compile_error!("Please add |_| to last argument to turn it to closure: \n      const ARR: [TYPE; SIZE] = const_arr!([TYPE; SIZE], INIT_FN);\n e.g. const ARR: [i32;  10  ] = const_arr!([i32;  10  ], |i| i as i32);"); };
161    ($type:ty) => {compile_error!("Array type has wrong format. It should be [TYPE; SIZE]: \n      const ARR: [TYPE; SIZE] = const_arr!([TYPE; SIZE], INIT_FN);\n e.g. const ARR: [i32;  10  ] = const_arr!([i32;  10  ], |i| i as i32);"); };
162    ($type:ty, ) => {compile_error!("Array type has wrong format. It should be [TYPE; SIZE]: \n      const ARR: [TYPE; SIZE] = const_arr!([TYPE; SIZE], INIT_FN);\n e.g. const ARR: [i32;  10  ] = const_arr!([i32;  10  ], |i| i as i32);"); };
163    ($type:ty,$size:literal) => {compile_error!("Array type has wrong format. It should be [TYPE; SIZE]: \n      const ARR: [TYPE; SIZE] = const_arr!([TYPE; SIZE], INIT_FN);\n e.g. const ARR: [i32;  10  ] = const_arr!([i32;  10  ], |i| i as i32);"); };
164    ($type:ty,$size:literal, $_:tt) => {compile_error!("Array type has wrong format. It should be [TYPE; SIZE]: \n      const ARR: [TYPE; SIZE] = const_arr!([TYPE; SIZE], INIT_FN);\n e.g. const ARR: [i32;  10  ] = const_arr!([i32;  10  ], |i| i as i32);"); };
165    ($type:ty,$size:literal, |$_:tt| $_n2:tt) => {compile_error!("Array type has wrong format. It should be [TYPE; SIZE]: \n      const ARR: [TYPE; SIZE] = const_arr!([TYPE; SIZE], INIT_FN);\n e.g. const ARR: [i32;  10  ] = const_arr!([i32;  10  ], |i| i as i32);"); };
166}
167
168/// ### Wrapper around [`const_arr`] macro. Allows to specify the type of an array `single` time.
169/// #### Supports both `closure` syntax and `const fn` initialization.
170/// 
171/// Usage:
172/// ```ignore
173/// make_const_arr!(ARR_NAME, [TYPE; SIZE], CONST_INIT_FN);
174/// ```
175/// 
176/// Desugars to:
177/// ```ignore
178/// const ARR_NAME: [TYPE; SIZE] = const_arr!([TYPE; SIZE], CONST_INIT_FN);
179/// ```
180/// 
181/// - `CONST_INIT_FN` is const function or const-like closure from 
182/// `array index`(`usize`) to `TYPE`
183/// 
184/// Examples:
185/// ```
186/// use const_array_init::make_const_arr;
187/// 
188/// make_const_arr!(ARR1, [i32; 5], |i| i as i32 + 1);
189/// assert_eq!(ARR1, [1, 2, 3, 4, 5]);
190/// 
191/// const fn to_i32_plus_one(n: usize) -> i32 {
192///     n as i32 + 1
193/// }
194/// 
195/// make_const_arr!(ARR2, [i32; 5], to_i32_plus_one);
196/// assert_eq!(ARR2, [1, 2, 3, 4, 5]);
197/// ```
198#[macro_export]
199#[rustfmt::skip]
200macro_rules! make_const_arr {
201    ($NAME:ident, [$TYPE:ty; $SIZE:literal], $func_name:ident ) => {
202        const $NAME: [$TYPE; $SIZE] = {
203            // Create array of proper SIZE and initialize it with garbage data 
204            // using $func_name(0) call as if every value had index 0.
205            // 
206            // There is no way to create array without initializing it and
207            // we cannot initialize it with 0-s because it isn't always valid (e.g. references)
208            // and MaybeUninit is unsafe and unstable in const context.
209            const TEMP_ITEM: $TYPE = $func_name(0);
210            let mut arr: [$TYPE; $SIZE] = [TEMP_ITEM; $SIZE];
211
212            // Initialize array with proper data using $func_name(ind) call
213            let mut ind = 0;
214            while ind < $SIZE {
215                arr[ind] = $func_name(ind);
216                ind += 1;
217            }
218            arr
219        }
220    ;
221    };
222    ($NAME:ident, [$TYPE:ty; $SIZE:literal], |$name:ident| $body:expr ) => {
223        const $NAME: [$TYPE; $SIZE] = {
224            // Create array of proper SIZE and initialize it with garbage data 
225            // using $body with $name predefined to 0 as if every value had index 0.
226            // 
227            // There is no way to create array without initializing it and
228            // we cannot initialize it with 0-s because it isn't always valid (e.g. references)
229            // and MaybeUninit is is unsafe and unstable in const context.
230            #[allow(non_upper_case_globals)]
231            let mut arr: [$TYPE; $SIZE] = {
232                const $name: usize = 0;
233                const TEMP_ITEM: $TYPE = $body;
234                [TEMP_ITEM; $SIZE]
235            };
236
237            // Initialize array with proper data from closure's body
238            let mut $name = 0;
239            while $name < $SIZE {
240                arr[$name] = $body;
241                $name += 1;
242            }
243            arr
244        };
245    };
246    ($NAME:ident, [$TYPE:ty; $SIZE:literal], |_| $body:expr ) => {
247        const $NAME: [$TYPE; $SIZE] = {
248            const TEMP_ITEM: $TYPE = $body;
249            [TEMP_ITEM; $SIZE]
250        };
251    };
252    () => { compile_error!("Please specify array name ARR_NAME: \n      make_const_arr!(ARR_NAME, [TYPE; SIZE], INIT_FN);\n e.g. make_const_arr!(MY_ARR  , [i32;  1024], |i| i as i32);"); };
253    ($_:literal) => { compile_error!("Please specify array name ARR_NAME: \n      make_const_arr!(ARR_NAME, [TYPE; SIZE], INIT_FN);\n e.g. make_const_arr!(MY_ARR  , [i32;  1024], |i| i as i32);"); };
254    ($NAME:ident) => { compile_error!("Please specify array type TYPE: \n      make_const_arr!(ARR_NAME, [TYPE; SIZE], INIT_FN);\n e.g. make_const_arr!(MY_ARR  , [i32;  1024], |i| i as i32);"); };
255    ($NAME:ident, ) => { make_const_arr!($NAME); };
256    ($NAME:ident, [$type:ty]) => { compile_error!("Please add SIZE to array type: It should be [TYPE; SIZE]: \n      make_const_arr!(ARR_NAME, [TYPE; SIZE], INIT_FN);\n e.g. make_const_arr!(MY_ARR  , [i32;  1024], |i| i as i32);"); };
257    ($NAME:ident, [$type:ty;]) => { compile_error!("Please add SIZE to array type: It should be [TYPE; SIZE]: \n      make_const_arr!(ARR_NAME, [TYPE; SIZE], INIT_FN);\n e.g. make_const_arr!(MY_ARR  , [i32;  1024], |i| i as i32);"); };
258    ($NAME:ident, [$type:ty;$size:literal]) => { compile_error!("Please specify init function INIT_FN: \n      make_const_arr!(ARR_NAME, [TYPE; SIZE], INIT_FN);\n e.g. make_const_arr!(MY_ARR  , [i32;  1024], |i| i as i32);"); };
259    ($NAME:ident, [$type:ty;$size:literal], $num:literal) => { compile_error!("Please add |_| to last argument to turn it to closure: \n      make_const_arr!(ARR_NAME, [TYPE; SIZE], INIT_FN);\n e.g. make_const_arr!(MY_ARR  , [i32;  1024], |i| i as i32);"); };
260    ($NAME:ident, $_:tt) => { compile_error!("Array type has wrong format. It should be [TYPE; SIZE]: \n      make_const_arr!(ARR_NAME, [TYPE; SIZE], INIT_FN);\n e.g. make_const_arr!(MY_ARR  , [i32;  1024], |i| i as i32);"); };
261    ($NAME:ident, $_n1:tt, $_n2:tt) => { compile_error!("Array type has wrong format. It should be [TYPE; SIZE]: \n      make_const_arr!(ARR_NAME, [TYPE; SIZE], INIT_FN);\n e.g. make_const_arr!(MY_ARR  , [i32;  1024], |i| i as i32);"); };($NAME:ident, $_n1:tt, $_n2:tt, $_n3:tt) => { compile_error!("Array type has wrong format. It should be [TYPE; SIZE]: \n      make_const_arr!(ARR_NAME, [TYPE; SIZE], INIT_FN);\n e.g. make_const_arr!(MY_ARR  , [i32;  1024], |i| i as i32);"); };
262    ($NAME:ident, $_n1:tt, $_n2:tt, $_fn_name:ident) => { compile_error!("Array type has wrong format. It should be [TYPE; SIZE]: \n      make_const_arr!(ARR_NAME, [TYPE; SIZE], INIT_FN);\n e.g. make_const_arr!(MY_ARR  , [i32;  1024], |i| i as i32);"); };
263    ($NAME:ident, $_n1:tt, $_n2:tt, |$_cl:tt| $_b:tt) => { compile_error!("Array type has wrong format. It should be [TYPE; SIZE]: \n      make_const_arr!(ARR_NAME, [TYPE; SIZE], INIT_FN);\n e.g. make_const_arr!(MY_ARR  , [i32;  1024], |i| i as i32);"); };
264}