array_macro/lib.rs
1// SPDX-FileCopyrightText: 2017 - 2022 Kamila Borowska <kamila@borowska.pw>
2//
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5//! Array multiple elements constructor syntax.
6//!
7//! While Rust does provide those, they require copy, and you cannot obtain the
8//! index that will be created. This crate provides syntax that fixes both of
9//! those issues.
10//!
11//! # Examples
12//!
13//! ```
14//! # #[macro_use]
15//! # extern crate array_macro;
16//! # fn main() {
17//! assert_eq!(array![String::from("x"); 2], [String::from("x"), String::from("x")]);
18//! assert_eq!(array![x => x; 3], [0, 1, 2]);
19//! # }
20//! ```
21
22#![no_std]
23#![deny(missing_docs)]
24
25#[doc(hidden)]
26pub extern crate core as __core;
27
28/// Creates an array containing the arguments.
29///
30/// This macro provides a way to repeat the same macro element multiple times
31/// without requiring `Copy` implementation as array expressions require.
32///
33/// There are two forms of this macro.
34///
35/// - Create an array from a given element and size. This will `Clone` the element.
36///
37/// ```
38/// use array_macro::array;
39/// assert_eq!(array![vec![1, 2, 3]; 2], [[1, 2, 3], [1, 2, 3]]);
40/// ```
41///
42/// Unlike array expressions this syntax supports all elements which implement
43/// `Clone`.
44///
45/// - Create an array from a given expression that is based on index and size.
46/// This doesn't require the element to implement `Clone`.
47///
48/// ```
49/// use array_macro::array;
50/// assert_eq!(array![x => x * 2; 3], [0, 2, 4]);
51/// ```
52///
53/// This form can be used for declaring `const` variables.
54///
55/// ```
56/// use array_macro::array;
57/// const ARRAY: [String; 3] = array![_ => String::new(); 3];
58/// assert_eq!(ARRAY, ["", "", ""]);
59/// ```
60///
61/// # Limitations
62///
63/// When using a form with provided index it's not possible to use `break`
64/// or `continue` without providing a label. This won't compile.
65///
66/// ```compile_fail
67/// use array_macro::array;
68/// loop {
69/// array![_ => break; 1];
70/// }
71/// ```
72///
73/// To work-around this issue you can provide a label.
74///
75/// ```
76/// use array_macro::array;
77/// 'label: loop {
78/// array![_ => break 'label; 1];
79/// }
80/// ```
81#[macro_export]
82macro_rules! array {
83 [$expr:expr; $count:expr] => {{
84 let value = $expr;
85 $crate::array![_ => $crate::__core::clone::Clone::clone(&value); $count]
86 }};
87 [$i:pat => $e:expr; $count:expr] => {
88 $crate::__array![$i => $e; $count]
89 };
90}
91
92use core::mem::{ManuallyDrop, MaybeUninit};
93use core::ptr;
94
95#[doc(hidden)]
96#[repr(transparent)]
97pub struct __ArrayVec<T, const N: usize>(pub __ArrayVecInner<T, N>);
98
99impl<T, const N: usize> Drop for __ArrayVec<T, N> {
100 fn drop(&mut self) {
101 // This is safe as arr[..len] is initialized due to
102 // __ArrayVecInner's type invariant.
103 let initialized = &mut self.0.arr[..self.0.len] as *mut _ as *mut [T];
104 unsafe { ptr::drop_in_place(initialized) };
105 }
106}
107
108// Type invariant: arr[..len] must be initialized
109#[doc(hidden)]
110#[non_exhaustive]
111pub struct __ArrayVecInner<T, const N: usize> {
112 pub arr: [MaybeUninit<T>; N],
113 pub len: usize,
114 // This field exists so that array! macro could retrieve the value of N.
115 // The method to retrieve N cannot be directly on __ArrayVecInner as
116 // borrowing it could cause a reference to interior mutable data to
117 // be created which is not allowed in `const fn`.
118 //
119 // Because this field doesn't actually store anything it's not possible
120 // to replace it in an already existing instance of __ArrayVecInner.
121 pub capacity: __Capacity<N>,
122}
123
124impl<T, const N: usize> __ArrayVecInner<T, N> {
125 #[doc(hidden)]
126 pub const unsafe fn new(arr: [MaybeUninit<T>; N]) -> Self {
127 Self {
128 arr,
129 len: 0,
130 capacity: __Capacity,
131 }
132 }
133}
134
135#[doc(hidden)]
136pub struct __Capacity<const N: usize>;
137
138impl<const N: usize> __Capacity<N> {
139 #[doc(hidden)]
140 pub const fn get(&self) -> usize {
141 N
142 }
143}
144#[doc(hidden)]
145#[repr(C)]
146pub union __Transmuter<T, const N: usize> {
147 pub init_uninit_array: ManuallyDrop<MaybeUninit<[T; N]>>,
148 pub uninit_array: ManuallyDrop<[MaybeUninit<T>; N]>,
149 pub out: ManuallyDrop<[T; N]>,
150}
151
152#[doc(hidden)]
153#[repr(C)]
154pub union __ArrayVecTransmuter<T, const N: usize> {
155 pub vec: ManuallyDrop<__ArrayVec<T, N>>,
156 pub inner: ManuallyDrop<__ArrayVecInner<T, N>>,
157}
158
159#[doc(hidden)]
160#[macro_export]
161macro_rules! __array {
162 [$i:pat => $e:expr; $count:expr] => {{
163 let mut vec = $crate::__ArrayVec::<_, {$count}>(unsafe { $crate::__ArrayVecInner::new(
164 // An uninitialized `[MaybeUninit<_>; LEN]` is valid.
165 $crate::__core::mem::ManuallyDrop::into_inner(unsafe {
166 $crate::__Transmuter {
167 init_uninit_array: $crate::__core::mem::ManuallyDrop::new($crate::__core::mem::MaybeUninit::uninit()),
168 }
169 .uninit_array
170 }),
171 )});
172 while vec.0.len < (&vec.0.capacity).get() {
173 let $i = vec.0.len;
174 let _please_do_not_use_continue_without_label;
175 let value;
176 struct __PleaseDoNotUseBreakWithoutLabel;
177 loop {
178 _please_do_not_use_continue_without_label = ();
179 value = $e;
180 break __PleaseDoNotUseBreakWithoutLabel;
181 };
182 // This writes an initialized element.
183 vec.0.arr[vec.0.len] = $crate::__core::mem::MaybeUninit::new(value);
184 // We just wrote a valid element, so we can add 1 to len, it's valid.
185 vec.0.len += 1;
186 }
187 // When leaving this loop, vec.0.len must equal to $count due
188 // to loop condition. It cannot be more as len is increased by 1
189 // every time loop is iterated on, and $count never changes.
190
191 // __ArrayVec is representation compatible with __ArrayVecInner
192 // due to #[repr(transparent)] in __ArrayVec.
193 let inner = $crate::__core::mem::ManuallyDrop::into_inner(unsafe {
194 $crate::__ArrayVecTransmuter {
195 vec: $crate::__core::mem::ManuallyDrop::new(vec),
196 }
197 .inner
198 });
199 // At this point the array is fully initialized, as vec.0.len == $count,
200 // so converting an array of potentially uninitialized elements into fully
201 // initialized array is safe.
202 $crate::__core::mem::ManuallyDrop::into_inner(unsafe {
203 $crate::__Transmuter {
204 uninit_array: $crate::__core::mem::ManuallyDrop::new(inner.arr),
205 }
206 .out
207 })
208 }}
209}