array_lit/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]
2
3//! Macros for array and `Vec` literals with superpowers.
4//! The macro for arrays is called `arr!`; the macro for `Vec` is called `vec!`
5//! and shadows the macro from the standard library.
6//!
7//! They allow creating an array or `Vec` where only some elements are
8//! specified, and the rest is set to a default value.
9//!
10//! The following macro specifies three consecutive values at index 0:
11//!
12//! ```
13//! # use array_lit::arr;
14//! let a = arr![0; 8; { [0]: [1, 2, 3] }];
15//! assert_eq!(a, [1, 2, 3, 0, 0, 0, 0, 0]);
16//! ```
17//!
18//! The square brackets are only needed when specifying multiple consecutive
19//! elements:
20//!
21//! ```
22//! # use array_lit::arr;
23//! let a = arr![1; 8; { 6: 0 }];
24//! assert_eq!(a, [1, 1, 1, 1, 1, 1, 0, 1]);
25//! ```
26//!
27//! You can specify as many single and consecutive values as you like:
28//!
29//! ```
30//! # use array_lit::arr;
31//! let a = arr![0; 8; {
32//! 6: 1, // 1 at index 6
33//! [2]: [3, 4], // 3 and 4 starting from index 2
34//! 7: 5, // 5 at index 7
35//! }];
36//! assert_eq!(a, [0, 0, 3, 4, 0, 0, 1, 5]);
37//! ```
38//!
39//! The familiar array syntax (`arr![a; N]` and `arr![a, b, c]`) is also
40//! supported, so the `vec!` macro from this crate is a drop-in replacement for
41//! `std::vec!`.
42//!
43//! ## How does it work?
44//!
45//! The macros generate a block that first creates a array or `Vec`, and then
46//! inserts the specified values:
47//!
48//! ```
49//! # use array_lit::arr;
50//! arr![4; 5; { 0: 0, 1: 1 }];
51//! // is expanded to
52//! {
53//! let mut arr = [4; 5]; // in the vec! macro, std::vec! is used
54//! arr[0] = 0;
55//! arr[1] = 1;
56//! arr
57//! };
58//! ```
59//!
60//! If an array is inserted that is not of the form `[a, b, c, ..]`, a loop is
61//! used:
62//!
63//! ```rust
64//! # use array_lit::arr;
65//! arr![4; 10; { [1]: [2; 4] }];
66//! // is expanded to
67//! {
68//! let mut arr = [4; 10];
69//! let mut i = 1;
70//! let end = i + 4;
71//! while i < end {
72//! arr[i] = 2;
73//! i += 1;
74//! }
75//! arr
76//! };
77//! ```
78//!
79//! This even works for slices, arrays and `Vec`s created at runtime:
80//!
81//! ```
82//! # use array_lit::arr;
83//! let my_slice = &[1, 2, 3, 4];
84//! arr![4; 10; { [1]: my_slice }];
85//! ```
86//!
87//! ## What about array lifetimes?
88//!
89//! In trivial cases such as `arr![3; 5]`, the `'static` lifetime is inferred
90//! for array literals. This means that they have the exact same behavior as
91//! normal array literals.
92//!
93//! In the other cases, the `'static` lifetime is not inferred. This means that
94//! the array literal is computed at runtime and doesn't have a fixed memory
95//! location. It also means that the following
96//!
97//! ```compile_fail
98//! # use array_lit::arr;
99//! fn return_temporary() -> &'static [i32; 4] {
100//! &arr![0; 4; { 0: 1 }]
101//! }
102//! ```
103//!
104//! produces `error[E0515]: cannot return reference to temporary value`. This
105//! can be solved by assigning the literal to a `const` or `static` variable
106//! first:
107//!
108//! ```
109//! # use array_lit::arr;
110//! fn return_temporary() -> &'static [i32; 4] {
111//! static ARR: &[i32; 4] = &arr![0; 4; { 0: 1 }];
112//! ARR
113//! }
114//! ```
115//!
116//! Values assigned to a `static` or `const` variable must be constant. Due to
117//! limitations in the compiler, macros that expand to loops aren't allowed
118//! there:
119//!
120//! ```compile_fail
121//! const ARR: [i32; 4] = arr![0; 16; { [0]: [1; 8] }];
122//! // this is expanded to a loop ~~~~~~~~~~~^^^^^^
123//! ```
124//!
125//! Note that `const` enforces **const evaluation**, which means that the whole
126//! array is included in the application binary. This might not be desirable if
127//! the array is large.
128//!
129//! ## Usage
130//!
131//! Import the macros with
132//!
133//! ```ignore
134//! use array_lit::{arr, vec};
135//! ```
136//!
137//! If you don't want to shadow `std::vec!`, you can rename the macro:
138//!
139//! ```ignore
140//! use array_lit::{arr, vec as vector};
141//! ```
142//!
143//! Importing the macros globally is also supported, although not recommended:
144//!
145//! ```ignore
146//! #[macro_use]
147//! extern crate array_lit;
148//! ```
149//!
150//! ## Custom indices
151//!
152//! If you want to use your own `Index`/`IndexMut` implementation in these
153//! macros, you probably need an extra pair of parentheses:
154//!
155//! ```ignore
156//! #[derive(Copy, Clone)]
157//! struct S(bool);
158//!
159//! /// Your custom index
160//! struct Idx(usize);
161//!
162//! impl std::ops::Index<Idx> for Vec<S> {
163//! type Output = bool;
164//! // etc.
165//! }
166//!
167//! impl std::ops::IndexMut<Idx> for Vec<S> {
168//! // etc.
169//! }
170//!
171//! vec![S(true); 1000; { (Idx(16)): false }];
172//! // parens needed ~~~~~^~~~~~~~^
173//! ```
174//!
175//! ## `no_std` support
176//!
177//! This library supports `no_std`, if default features are disabled.
178//! This makes the `vec!` macro unavailable.
179//!
180//! ## Minimum required Rust version
181//!
182//! Requires Rust 1.33.
183
184#[cfg(test)]
185mod tests;
186
187/// A macro for array literals with superpowers.
188///
189/// See [the module level documentation](index.html) for more.
190///
191/// # Example
192///
193///```rust
194/// # use array_lit::arr;
195/// let a = arr![1; 5; { [0]: [1, 2], 4: 0 }];
196/// assert_eq!(a, [1, 2, 1, 1, 0]);
197/// ```
198#[macro_export]
199macro_rules! arr {
200 [$item:expr ; $len:expr ; { $( $index:tt : $value:expr ),* $(,)? }] => {
201 {
202 #[allow(unused_mut, unused_assignments)]
203 {
204 let mut arr = [$item ; $len];
205 $( $crate::arr!(impl arr { $index : $value }); )*
206 arr
207 }
208 }
209 };
210
211 // same syntax as regular array literals:
212 [$item:expr ; $len:expr] => {
213 [$item ; $len]
214 };
215 [$( $item:expr ),* $(,)?] => {
216 [ $($item),* ]
217 };
218
219 // Implementation details:
220 (impl $arr:ident { [$start:tt] : [ $value:expr ; $len:expr ] }) => {
221 let mut i = $start;
222 let end = i + $len;
223 while i < end {
224 $arr[i] = $value;
225 i += 1;
226 }
227 };
228 (impl $arr:ident { [$start:tt] : [ $($value:expr),* $(,)? ] }) => {
229 let mut i = $start;
230 $(
231 $arr[i] = $value;
232 i += 1;
233 )*
234 };
235 (impl $arr:ident { [$start:tt] : $value:expr }) => {
236 let mut i = $start;
237 let start = i;
238 let arr_inner = $value;
239 let end = i + arr_inner.len();
240 while i < end {
241 $arr[i] = arr_inner[i - start];
242 i += 1;
243 }
244 };
245 (impl $arr:ident { $key:tt : $value:expr }) => {
246 $arr[$key] = $value;
247 };
248}
249
250/// A macro for `Vec` literals with superpowers.
251///
252/// See [the module level documentation](index.html) for more.
253///
254/// > This macro requires the **`std`** feature (enabled by default)
255///
256/// # Example
257///
258///```rust
259/// # use array_lit::vec;
260/// let a = vec![1; 5; { [0]: [1, 2], 4: 0 }];
261/// assert_eq!(a, std::vec![1, 2, 1, 1, 0]);
262/// ```
263#[cfg(feature = "std")]
264#[macro_export]
265macro_rules! vec {
266 [$item:expr ; $len:expr ; { $( $index:tt : $value:expr ),* $(,)? }] => {
267 {
268 #[allow(unused_mut, unused_assignments)]
269 {
270 let mut vec = std::vec![$item ; $len];
271 $( $crate::arr!(impl vec { $index : $value }); )*
272 vec
273 }
274 }
275 };
276
277 // same syntax as regular array literals:
278 [$item:expr ; $len:expr] => {
279 std::vec![$item ; $len]
280 };
281 [$( $item:expr ),* $(,)?] => {
282 std::vec![ $($item),* ]
283 };
284}