What's currently the best way to initiliaze an array with non Copy, non Default types?

Has const generics changed anything?

Is the blog post behind arr_macro still the most up-to-date authority on the matter?

I'm unable to use the arr_macro anyway, because I'm unable to feed it an integer literal for its length.

I don't think const generics have changed anything on this front. In what form do you have your length?

My length is determined by the number of variants in an enum, which I obtain a length from using strum_macros::EnumCount (so I can write DRY code).

There is another option than the ones listed in the article. This is that, even for non-copy types, the [value; count] syntax will compile if the value refers to a const. For example:

enum MyEnum {
    A, B, C, D,
}

fn enum_array() -> [MyEnum; 1000] {
    const HELPER: MyEnum = MyEnum::A;
    
    [HELPER; 1000]
}

Interesting, but the type I wish to store is not an enum, nor able to be const, only the length is generated from the enum.

I have solved it like this:

let locations = {
    let mut data: [std::mem::MaybeUninit<WidgetPod<(), Button<()>>>; Location::COUNT] =
        unsafe { std::mem::MaybeUninit::uninit().assume_init() };
    for (i, l) in Location::iter().enumerate() {
        unsafe {
            std::ptr::write(
                data[i].as_mut_ptr(),
                WidgetPod::new(Button::new(l.to_string())),
            );
        }
    }
    unsafe { std::mem::transmute::<_, [WidgetPod<(), Button<()>>; Location::COUNT]>(data) }
};

It's pretty obvious (to me) that it's safe, but I don't know what SAFTEY comment to write.

Well for the ptr::write, your safety comment should be that data[i] is a valid memory location. As for the transmute, you must argue that all elements in the array have been initialized. So for example, you are relying on the fact that Location::iter() will actually iterate Location::COUNT times.

Note also that if the Location::iter() iterator or your calls to new panic, then you are potentially leaking memory as the elements already written wont have their destructor called. (but this wont be unsafe, only unpleasant)

2 Likes

Thank you for going over the unsafe comments with me. That was really helpful!

Regarding the leaking...Right, but if it panics (which it won't unless there's an alien invasion or something), then the whole app will just unwind and close anyway and the OS will free the leak, no?

Sure, unless you catch the panic or similar.

Actually, const generics does change one thing, namely it is now possible to define something like this.

3 Likes

What is this dark magic ye summon?

There was a mistake in the destructor. It is fixed here: playground

3 Likes

Also with const generics, we could add a function

<T, const N: usize>(impl FnMut() -> T) -> [T; N]

to the standard library.

2 Likes

That's quite nice. It's also easy to add Extend and FromIterator implementations. (Playground)

With a little more work, this could effectively be a stack-allocated Vec with a fixed capacity. (Edit: Apparently, this already exists, per @steffahn )

That’s basically the new ArrayVec, right?

2 Likes

It looks like there's no way to convert a full ArrayVec into a [T;CAP]. Maybe it should have a TryInto implementation for that.

There is.

2 Likes

Ah yeah, I had not seen that update of ArrayVec. But yes, it's a simpler version of that.

1 Like

Looking for existing PRs, I found this one

and this one