Uninitialized array

Is there such thing like uninitialized array? So for example I can implement scenario in which I have a container that had preallocated mem for a 1000 elems but non of them isn't initialized plus I'm able to place element in any of those 1000 indexes without the need to fill any other. So for example, I have an "array" that is uninitialized and I am able to put an element at index 500 whilst having all of the other indexes uninitialized.

Usually, I’d use Vec<Option<_>> for this, and initialize it with Nones. There’s also MaybeUninit, but that requires unsafe code and manual auditing that everything you try to read has been properly initialized.

Alternatively, if the uninitialized values are always at the end, you can use Vec::with_capacity() to allocate all of the space you need and then use push() to initialize the next free item.

From the standard library documentation:
Initializing an array element-by-element

Initializing an array element-by-element

MaybeUninit<T> can be used to initialize a large array element-by-element:

use std::mem::{self, MaybeUninit};

let data = {
    // Create an uninitialized array of `MaybeUninit`. The `assume_init` is
    // safe because the type we are claiming to have initialized here is a
    // bunch of `MaybeUninit`s, which do not require initialization.
    let mut data: [MaybeUninit<Vec<u32>>; 1000] = unsafe {
        MaybeUninit::uninit().assume_init()
    };

    // Dropping a `MaybeUninit` does nothing. Thus using raw pointer
    // assignment instead of `ptr::write` does not cause the old
    // uninitialized value to be dropped. Also if there is a panic during
    // this loop, we have a memory leak, but there is no memory safety
    // issue.
    for elem in &mut data[..] {
        *elem = MaybeUninit::new(vec![42]);
    }

    // Everything is initialized. Transmute the array to the
    // initialized type.
    unsafe { mem::transmute::<_, [Vec<u32>; 1000]>(data) }
};

assert_eq!(&data[0], &[42]);

You can also work with partially initialized arrays, which could
be found in low-level datastructures.

use std::mem::MaybeUninit;
use std::ptr;

// Create an uninitialized array of `MaybeUninit`. The `assume_init` is
// safe because the type we are claiming to have initialized here is a
// bunch of `MaybeUninit`s, which do not require initialization.
let mut data: [MaybeUninit<String>; 1000] = unsafe { MaybeUninit::uninit().assume_init() };
// Count the number of elements we have assigned.
let mut data_len: usize = 0;

for elem in &mut data[0..500] {
    *elem = MaybeUninit::new(String::from("hello"));
    data_len += 1;
}

// For each item in the array, drop if we allocated it.
for elem in &mut data[0..data_len] {
    unsafe { ptr::drop_in_place(elem.as_mut_ptr()); }
}
3 Likes

For

you can outsource that pattern with the

crate, which thus gets to offer a non-unsafe API :ok_hand:


That's indeed what the ManuallyDrop wrapper is for, but it is still somewhat easy to misuse. I recommend using the

crate, which provides stable versions of not-yet-stable stdlib APIs, plus its own share of abstraction that helps avoiding many footguns from dealing with uninit memory :slightly_smiling_face:

For instance:

use ::uninit::prelude::*;

let mut array = uninit_array![u8; 1_000];
array[500] = MaybeUninit::new(42); // initialize the 501-th element
// Now we can `unsafe`-ly assert that that elem has been init.
let that_elem: &mut u8 = unsafe {
    ::core::mem::transmute::<&mut MaybeUninit<u8>, &mut u8>(
        &mut array[500],
    )
;
assert_eq!(*that_elem, 42);

and more generally (no unsafe):

//! No `unsafe`!
use ::uninit::prelude::*;

let mut array = uninit_array![u8; 1_000];
// out pointer to a potentially uninitialised array
let mut buf: Out<'_, [u8]> = array.as_out();
let that_elem: &mut u8 = buf.r().get_out(500).unwrap().write(42);
assert_eq!(*that_elem, 42);
4 Likes

Hey, that's fantastic answer! Thanks a lot!

1 Like