Expand description
§Unarray
Helper utilities for working with arrays of uninitialized memory.
§Current stable Rust
Creating arrays in Rust can be somewhat painful. Currently, your best option in the general
case is to allocate your elements in a Vec
, then convert to an array:
const LEN: usize = 1000;
let mut elements = Vec::with_capacity(LEN); // heap allocation here
for i in 0..LEN {
elements.push(123);
}
let result: [i32; LEN] = elements.try_into().unwrap();
This needlessly allocates space on the heap, which is then immediately freed. If your type
implements Copy
, and has a sensible default value, you can avoid this allocation by creating
an array literal (e.g. [0; 1000]
), then iterating over each element and setting it, but this
also incurrs an unnecessary initialization cost. Why set each element to 0
, then set it
again, when you could just set it once?
§uninit_buf
and mark_initialized
The lowest-level tools provided by this library are the pair of functions: uninit_buf
and
mark_initialized
. These are ergonomic wrappers around the core::mem::MaybeUninit
type.
Roughly speaking, most uses of these functions will follow the following steps:
- Stack-allocate a region of uninitialized memory with
uninit_buf
- Initialize each element
- Unsafely declare that all elements are initialized using
mark_initialized
For example:
let mut buffer = uninit_buf();
for elem in &mut buffer {
elem.write(123);
}
let result = unsafe { mark_initialized(buffer) };
assert_eq!(result, [123; 1000]);
These functions closely map onto tools provided by core::mem::MaybeUninit
, so should feel
familiar. However, mark_initialized
is an unsafe function, since it’s possible to create
uninitialized values that aren’t wrapped in MaybeUninit
. It’s up to the programmer to make
sure every element has been initialized before calling mark_initialized
, otherwise it’s UB.
For this, there are also fully safe APIs that cover some of the common patterns via an
extension trait on [T; N]
:
§UnarrayArrayExt
extension trait
// mapping an array via a `Result`
let strings = ["123", "234"];
let numbers = strings.map_result(|s| s.parse());
assert_eq!(numbers, Ok([123, 234]));
let bad_strings = ["123", "uh oh"];
let result = bad_strings.map_result(|s| s.parse::<i32>());
assert!(result.is_err()); // since one of the element fails, the whole operation fails
There is also map_option
for functions which return an Option
§Collecting iterators
Iterators generally don’t know their length at compile time. But it’s often the case that the programmer knows the length ahead of time. In cases like this, it’s common to want to collect these elements into an array, without heap allocation or initializing default elements.
Arrays don’t implement FromIterator
for this very reason. So this library provides
ArrayFromIter
:
let iter = [1, 2, 3].into_iter().map(|i| i * 2);
let ArrayFromIter(array) = iter.collect(); // inferred to be `ArrayFromIter::<i32, 3>`
assert_eq!(array, Some([2, 4, 6]));
However, this can fail, since the iterator may not actually yield the right number of elements.
In these cases, the inner option is None
:
let iter = [1, 2, 3, 4].into_iter();
match iter.collect() {
ArrayFromIter(Some([a, b, c])) => println!("3 elements, {a}, {b}, {c}"),
ArrayFromIter(None) => println!("not 3 elements"),
}
§build_array-*
functions
Finally, it’s often the case that you want to initialize each array element based on its index.
For that, build_array
takes a const generic length, and a function that takes an index and
returns an element, and builds the array for you:
use unarray::*;
let array: [usize; 5] = build_array(|i| i * 2);
assert_eq!(array, [0, 2, 4, 6, 8]);
There are also variants that allow fallibly constructing an array, via build_array_result
or build_array_option
, similar to UnarrayArrayExt::map_result
and UnarrayArrayExt::map_option
.
Structs§
- A wrapper type to collect an
Iterator
into an array
Traits§
- An extension trait that adds methods to
[T; N]
Functions§
- Build an array with a function that creates elements based on their index
- Build an array with a function that creates elements based on their value, short-circuiting if any index returns a
None
- Build an array with a function that creates elements based on their value, short-circuiting if any index returns an
Err
- Convert a
[MaybeUninit<T>; N]
to a[T; N]
- Create an array of unintialized memory