unarray/
lib.rs

1//! # Unarray
2//!
3//! Helper utilities for working with arrays of uninitialized memory.
4//!
5//! ## Current stable Rust
6//!
7//! Creating arrays in Rust can be somewhat painful. Currently, your best option in the general
8//! case is to allocate your elements in a `Vec`, then convert to an array:
9//! ```
10//! # use core::convert::TryInto;
11//! const LEN: usize = 1000;
12//! let mut elements = Vec::with_capacity(LEN);  // heap allocation here
13//!
14//! for i in 0..LEN {
15//!   elements.push(123);
16//! }
17//!
18//! let result: [i32; LEN] = elements.try_into().unwrap();
19//! ```
20//! This needlessly allocates space on the heap, which is then immediately freed. If your type
21//! implements `Copy`, and has a sensible default value, you can avoid this allocation by creating
22//! an array literal (e.g. `[0; 1000]`), then iterating over each element and setting it, but this
23//! also incurrs an unnecessary initialization cost. Why set each element to `0`, then set it
24//! again, when you could just set it once?
25//!
26//! ## `uninit_buf` and `mark_initialized`
27//!
28//! The lowest-level tools provided by this library are the pair of functions: [`uninit_buf`] and
29//! [`mark_initialized`]. These are ergonomic wrappers around the [`core::mem::MaybeUninit`] type.
30//! Roughly speaking, most uses of these functions will follow the following steps:
31//!  - Stack-allocate a region of uninitialized memory with [`uninit_buf`]
32//!  - Initialize each element
33//!  - Unsafely declare that all elements are initialized using [`mark_initialized`]
34//!
35//! For example:
36//! ```
37//! # use unarray::*;
38//! let mut buffer = uninit_buf();  
39//!
40//! for elem in &mut buffer {
41//!   elem.write(123);  
42//! }
43//!
44//! let result = unsafe { mark_initialized(buffer) };
45//! assert_eq!(result, [123; 1000]);
46//! ```
47//! These functions closely map onto tools provided by [`core::mem::MaybeUninit`], so should feel
48//! familiar. However, [`mark_initialized`] is an unsafe function, since it's possible to create
49//! uninitialized values that aren't wrapped in `MaybeUninit`. It's up to the programmer to make
50//! sure every element has been initialized before calling [`mark_initialized`], otherwise it's UB.
51//!
52//! For this, there are also fully safe APIs that cover some of the common patterns via an
53//! extension trait on `[T; N]`:
54//!
55//! ## `UnarrayArrayExt` extension trait
56//!
57//! ```
58//! # use unarray::*;
59//! // mapping an array via a `Result`
60//! let strings = ["123", "234"];
61//! let numbers = strings.map_result(|s| s.parse());
62//! assert_eq!(numbers, Ok([123, 234]));
63//!
64//! let bad_strings = ["123", "uh oh"];
65//! let result = bad_strings.map_result(|s| s.parse::<i32>());
66//! assert!(result.is_err());  // since one of the element fails, the whole operation fails
67//! ```
68//! There is also `map_option` for functions which return an `Option`
69//!
70//! ## Collecting iterators
71//!
72//! Iterators generally don't know their length at compile time. But it's often the case that the
73//! programmer knows the length ahead of time. In cases like this, it's common to want to collect
74//! these elements into an array, without heap allocation or initializing default elements.
75//!
76//! Arrays don't implement `FromIterator` for this very reason. So this library provides
77//! `ArrayFromIter`:
78//! ```
79//! # use unarray::*;
80//! let iter = [1, 2, 3].into_iter().map(|i| i * 2);
81//! let ArrayFromIter(array) = iter.collect();  // inferred to be `ArrayFromIter::<i32, 3>`
82//! assert_eq!(array, Some([2, 4, 6]));
83//! ```
84//! However, this can fail, since the iterator may not actually yield the right number of elements.
85//! In these cases, the inner option is `None`:
86//! ```
87//! # use unarray::*;
88//! let iter = [1, 2, 3, 4].into_iter();
89//! match iter.collect() {
90//!   ArrayFromIter(Some([a, b, c])) => println!("3 elements, {a}, {b}, {c}"),
91//!   ArrayFromIter(None) => println!("not 3 elements"),
92//! }
93//! ```
94//! ## `build_array-*` functions
95//!
96//! Finally, it's often the case that you want to initialize each array element based on its index.
97//! For that, [`build_array`] takes a const generic length, and a function that takes an index and
98//! returns an element, and builds the array for you:
99//! ```
100//! use unarray::*;
101//! let array: [usize; 5] = build_array(|i| i * 2);
102//! assert_eq!(array, [0, 2, 4, 6, 8]);
103//! ```
104//! There are also variants that allow fallibly constructing an array, via [`build_array_result`]
105//! or [`build_array_option`], similar to [`UnarrayArrayExt::map_result`] and [`UnarrayArrayExt::map_option`].
106
107#![cfg_attr(not(test), no_std)]
108#![deny(clippy::missing_safety_doc, missing_docs)]
109use core::mem::MaybeUninit;
110
111mod build;
112mod map;
113mod from_iter;
114#[cfg(test)]
115mod testing;
116
117pub use build::{build_array, build_array_option, build_array_result};
118pub use map::UnarrayArrayExt;
119pub use from_iter::ArrayFromIter;
120
121/// Convert a `[MaybeUninit<T>; N]` to a `[T; N]`
122///
123/// ```
124/// # use unarray::*;
125/// let mut buffer = uninit_buf::<i32, 1000>();
126///
127/// for elem in &mut buffer {
128///   elem.write(123);
129/// }
130///
131/// let result = unsafe { mark_initialized(buffer) };
132/// assert_eq!(result, [123; 1000])
133/// ```
134///
135/// This largely acts as a workaround to the fact that [`core::mem::transmute`] cannot be used with
136/// const generic arrays, as it can't prove they have the same size (even when intuitively they are
137/// the same, e.g. `[i32; N]` and `[u32; N]`).
138///
139/// This is similar to the nightly-only [`core::mem::MaybeUninit::array_assume_init`]
140///
141/// # Safety
142///
143/// Internally, this uses [`core::mem::transmute_copy`] to convert a `[MaybeUninit<T>; N]` to `[T; N]`.
144/// As such, you must make sure every element has been initialized before calling this function. If
145/// there are uninitialized elements in `src`, these will be converted to `T`s, which is UB. For
146/// example:
147/// ```no_run
148/// # use unarray::*;
149/// // ⚠️ This example produces UB ⚠️
150/// let bools = uninit_buf::<bool, 10>();
151/// let uh_oh = unsafe { mark_initialized(bools) };  // UB: creating an invalid instance
152/// if uh_oh[0] {                                    // double UB: reading from unintiailized memory
153///   // ...
154/// }
155/// ```
156/// Even if you never use a value, it's still UB. This is especially true for types with
157/// [`core::ops::Drop`] implementations:
158/// ```no_run
159/// # use unarray::*;
160/// // ⚠️ This example produces UB ⚠️
161/// let strings = uninit_buf::<String, 10>();
162/// let uh_oh = unsafe { mark_initialized(strings) };  // UB: creating an invalid instance
163///
164/// // uh_oh is dropped here, freeing memory at random addresses
165/// ```
166pub unsafe fn mark_initialized<T, const N: usize>(src: [MaybeUninit<T>; N]) -> [T; N] {
167    core::mem::transmute_copy::<[MaybeUninit<T>; N], [T; N]>(&src)
168}
169
170/// Create an array of unintialized memory
171///
172/// This function is just a safe wrapper around `MaybeUninit::uninit().assume_init()`, which is
173/// safe when used to create a `[MaybeUninit<T>; N]`, since this type explicitly requires no
174/// initialization
175///
176/// ```
177/// # use unarray::*;
178/// let mut buffer = uninit_buf::<i32, 1000>();
179///
180/// for elem in &mut buffer {
181///   elem.write(123);
182/// }
183///
184/// let result = unsafe { mark_initialized(buffer) };
185/// assert_eq!(result, [123; 1000])
186/// ```
187///
188/// This is similar to the nightly-only [`core::mem::MaybeUninit::uninit_array`]
189pub fn uninit_buf<T, const N: usize>() -> [MaybeUninit<T>; N] {
190    // SAFETY:
191    // This is safe because we are assuming that a `[MaybeUninit<T>; N]` is initialized. However,
192    // since `MaybeUninit` doesn't require initialization, doing nothing counts as "initializing",
193    // so this is always safe
194    unsafe { MaybeUninit::uninit().assume_init() }
195}