array_append/lib.rs
1//! # `array-append`
2//!
3//! `array-append` exports a small family of functions for working with
4//! const-generic array types:
5//!
6//! - [`concat`] which concatenates two arrays
7//! - [`push_right`] and [`push_left`] which add an element to the end or
8//! beginning of an array respectively
9//! - [`split`] and [`split_end`] which split an array into two arrays
10//! - [`pop_right`] and [`pop_left`] which separate the last or first element
11//! respectively from an array
12//!
13//! And a few aliases:
14//!
15//! - [`push`]/[`pop`] for [`push_right`]/[`pop_right`] respectively
16//! - [`unshift`]/[`shift`] for [`push_left`]/[`pop_left`] respectively
17//!
18//! This library requires a nightly compiler due to the use of
19//! `#![feature(generic_const_exprs)]`. All unsafe code has been verified to be
20//! sound by manual proof and Miri.
21//!
22//! This library does not yet require the standard library, but it is brought in
23//! anyway unless the `std` default feature is disabled. This is for
24//! forward-compatibility in case `std`-dependent code is ever added.
25//!
26//! ## Example
27//!
28//! Create a no-alloc builder:
29//!
30//! ```
31//! #![allow(incomplete_features)]
32//! #![feature(generic_const_exprs)]
33//!
34//! use array_append::push;
35//!
36//! #[derive(PartialEq, Debug)]
37//! struct Built<const N: usize> {
38//! whatever: [usize; N]
39//! }
40//!
41//! struct Builder<const N: usize> {
42//! whatever: [usize; N]
43//! }
44//!
45//! impl Builder<0> {
46//! pub fn new() -> Self {
47//! Self { whatever: [] }
48//! }
49//! }
50//!
51//! impl<const N: usize> Builder<N> {
52//! pub fn from_array(array: [usize; N]) -> Self {
53//! Self { whatever: array }
54//! }
55//!
56//! pub fn with_usize(self, new: usize) -> Builder<{N + 1}> {
57//! // Can't use `Self` here, because `Self` is `Builder<N>`
58//! Builder { whatever: push(self.whatever, new) }
59//! }
60//!
61//! pub fn build(self) -> Built<N> {
62//! Built { whatever: self.whatever }
63//! }
64//! }
65//!
66//! assert_eq!(
67//! Builder::new()
68//! .with_usize(1)
69//! .with_usize(2)
70//! .with_usize(3)
71//! .build(),
72//! Builder::from_array([1, 2, 3]).build()
73//! );
74//! ```
75
76#![allow(incomplete_features)]
77#![deny(missing_docs)]
78#![deny(rustdoc::missing_doc_code_examples)]
79
80#![cfg_attr(not(std), no_std)]
81#![feature(generic_const_exprs)]
82
83use core::mem::{ManuallyDrop, MaybeUninit};
84
85pub use push_right as push;
86pub use pop_right as pop;
87
88pub use push_left as unshift;
89pub use pop_left as shift;
90
91/// Concatenates the given array `a` with the given other array `b`. Returns a
92/// new array which contains all elements from `a` and `b`. No elements are
93/// dropped, copied or cloned.
94///
95/// # Example
96///
97/// ```
98/// # use array_append::concat;
99/// #
100/// let arr1 = [1usize, 2, 3, 4, 5];
101/// let arr2 = [6, 7, 8, 9, 10];
102/// let arr = concat(arr1, arr2);
103///
104/// assert_eq!(arr, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
105/// ```
106pub fn concat<const N: usize, const M: usize, T>(a: [T; N], b: [T; M]) -> [T; N + M] where [(); N + M]: {
107 unsafe {
108 let mut uninit = MaybeUninit::<[T; N + M]>::uninit();
109
110 let ptr = uninit.as_mut_ptr() as *mut T;
111 (ptr as *mut [T; N]).write(a);
112 (ptr.add(N) as *mut [T; M]).write(b);
113
114 uninit.assume_init()
115 }
116}
117
118/// Extends the given array `a` by the given element `b`. Returns a new array
119/// which is the direct extension of `a` by `b`. No elements are dropped, copied
120/// or cloned.
121///
122/// # Example
123///
124/// ```
125/// # use array_append::push_right;
126/// #
127/// let arr = [1usize, 2, 3, 4];
128/// let elem = 5;
129/// let appended = push_right(arr, elem);
130///
131/// assert_eq!(appended, [1, 2, 3, 4, 5]);
132/// ```
133pub fn push_right<const N: usize, T>(a: [T; N], b: T) -> [T; N + 1] where [(); N + 1]: {
134 concat(a, [b])
135}
136
137/// Extends the given element `b` by the given array `a`. Returns a new array
138/// which is the direct extension of `a` by `b`. No elements are dropped, copied
139/// or cloned.
140///
141/// # Example
142///
143/// ```
144/// # use array_append::push_left;
145/// #
146/// let arr = [2usize, 3, 4, 5];
147/// let elem = 1;
148/// let prepended = push_left(arr, elem);
149///
150/// assert_eq!(prepended, [1, 2, 3, 4, 5]);
151/// ```
152pub fn push_left<const N: usize, T>(a: [T; N], b: T) -> [T; 1 + N] where [(); 1 + N]: {
153 concat([b], a)
154}
155
156/// Splits the given array `a` at the given point `M`. Returns a tuple
157/// containing two arrays where the first element is an array containing all
158/// elements from `a[..M]` and the second element is an array containing all
159/// elements from `a[M..]`. No elements are dropped, copied or cloned.
160///
161/// # Example
162///
163/// ```
164/// # use array_append::split;
165/// #
166/// let arr = [1usize, 2, 3, 4, 5];
167///
168/// // Currently, the turbofish syntax is required. rustc still cannot infer
169/// // them even if you fully annotate these variables' types.
170/// let (left, right) = split::<5, 3, usize>(arr);
171///
172/// assert_eq!(left, [1, 2, 3]);
173/// assert_eq!(right, [4, 5]);
174/// ```
175pub fn split<const N: usize, const M: usize, T>(a: [T; N]) -> ([T; M], [T; N - M]) where [(); N - M]: {
176 unsafe {
177 let a = ManuallyDrop::new(a);
178 let ptr = a.as_ptr() as *const T;
179 let mut left = MaybeUninit::<[T; M]>::uninit();
180 let mut right = MaybeUninit::<[T; N - M]>::uninit();
181
182 left.as_mut_ptr().write((ptr as *const [T; M]).read());
183 right.as_mut_ptr().write((ptr.add(M) as *const [T; N - M]).read());
184
185 (left.assume_init(), right.assume_init())
186 }
187}
188
189/// Identical to [`split`], but splits starting from the end of the array, to
190/// hopefully help with compiler proofs (in cases like [`pop_right`]).
191///
192/// # Example
193///
194/// ```
195/// # use array_append::split_end;
196/// #
197/// let arr = [1usize, 2, 3, 4, 5];
198/// let (left, right) = split_end::<5, 3, usize>(arr);
199///
200/// assert_eq!(left, [1, 2]);
201/// assert_eq!(right, [3, 4, 5]);
202/// ```
203pub fn split_end<const N: usize, const M: usize, T>(a: [T; N]) -> ([T; N - M], [T; M]) where [(); N - M]: {
204 unsafe {
205 let a = ManuallyDrop::new(a);
206 let ptr = a.as_ptr() as *const T;
207 let mut left = MaybeUninit::<[T; N - M]>::uninit();
208 let mut right = MaybeUninit::<[T; M]>::uninit();
209
210 left.as_mut_ptr().write((ptr as *const [T; N - M]).read());
211 right.as_mut_ptr().write((ptr.add(N - M) as *const [T; M]).read());
212
213 (left.assume_init(), right.assume_init())
214 }
215}
216
217/// Pops one element from the end of the given array `a`, returning the rest of
218/// the array and the popped element in a tuple. No elements are dropped, copied
219/// or cloned.
220///
221/// # Example
222///
223/// ```
224/// # use array_append::pop_right;
225/// #
226/// let arr = [1usize, 2, 3, 4, 5];
227/// let (arr, end) = pop_right(arr);
228///
229/// assert_eq!(arr, [1, 2, 3, 4]);
230/// assert_eq!(end, 5);
231/// ```
232pub fn pop_right<const N: usize, T>(a: [T; N]) -> ([T; N - 1], T) {
233 let (left, [right]) = split_end::<N, 1, T>(a);
234 (left, right)
235}
236
237/// Identical to [`pop_right`], but pops from the left of the array instead.
238///
239/// # Example
240///
241/// ```
242/// # use array_append::pop_left;
243/// #
244/// let arr = [1usize, 2, 3, 4, 5];
245/// let (arr, start) = pop_left(arr);
246///
247/// assert_eq!(arr, [2, 3, 4, 5]);
248/// assert_eq!(start, 1);
249/// ```
250pub fn pop_left<const N: usize, T>(a: [T; N]) -> ([T; N - 1], T) {
251 let ([right], left) = split::<N, 1, T>(a);
252 (left, right)
253}