Search code examples
arraysrust

What is the proper way to initialize a fixed length array?


I'm having trouble initializing a fixed length array. My attempts so far all result in the same "use of possibly uninitialized variable: foo_array" error:

#[derive(Debug)]
struct Foo { a: u32, b: u32 }

impl Default for Foo {
    fn default() -> Foo { Foo{a:1, b:2} }
}

pub fn main() {
    let mut foo_array: [Foo; 10];

    // Do something here to in-place initialize foo_array?

    for f in foo_array.iter() {
        println!("{:?}", f);
    }
}
error[E0381]: use of possibly uninitialized variable: `foo_array`
  --> src/main.rs:13:14
   |
13 |     for f in foo_array.iter() {
   |              ^^^^^^^^^ use of possibly uninitialized `foo_array`

I implemented the Default trait, but Rust does not seem to call this by default akin to a C++ constructor.

What is the proper way to initialize a fixed length array? I'd like to do an efficient in-place initialization rather than some sort of copy.

Related: Why is the Copy trait needed for default (struct valued) array initialization?

Related: Is there a way to not have to initialize arrays twice?


Solution

  • The safe but somewhat inefficient solution:

    #[derive(Copy, Clone, Debug)]
    struct Foo {
        a: u32,
        b: u32,
    }
    
    fn main() {
        let mut foo_array = [Foo { a: 10, b: 10 }; 10];
    }
    

    Since you're specifically asking for a solution without copies:

    use std::mem::MaybeUninit;
    
    #[derive(Debug)]
    struct Foo {
        a: u32,
        b: u32,
    }
    
    // We're just implementing Drop to prove there are no unnecessary copies.
    impl Drop for Foo {
        fn drop(&mut self) {
            println!("Destructor running for a Foo");
        }
    }
    
    pub fn main() {
        let array = {
            // Create an array of uninitialized values.
            let mut array: [MaybeUninit<Foo>; 10] = unsafe { MaybeUninit::uninit().assume_init() };
    
            for (i, element) in array.iter_mut().enumerate() {
                let foo = Foo { a: i as u32, b: 0 };
                *element = MaybeUninit::new(foo);
            }
    
            unsafe { std::mem::transmute::<_, [Foo; 10]>(array) }
        };
    
        for element in array.iter() {
            println!("{:?}", element);
        }
    }
    

    This is recommended by the documentation of MaybeUninit.