use core::mem::{transmute, MaybeUninit};
fn default_array<T: Default + Sized, const N: usize>() -> [T; N] {
unsafe {
let mut arr: [MaybeUninit<T>; N] = MaybeUninit::uninit().assume_init();
for ele in arr.iter_mut() {
*ele = MaybeUninit::new(T::default());
}
transmute(arr) // cannot transmute between types of different sizes, or dependently-sized types
}
}
In that Issue #61956, They mention a workaround like this:
// Using &mut as an assertion of unique "ownership"
let ptr = &mut arr as *mut _ as *mut [T; N];
let res = unsafe { ptr.read() };
core::mem::forget(arr);
res
My question is, "Does this horribly solution also work for Non-Copy type ?"
As I know that, ptr::read, Copy the contain without moving it. Something like mem::transmute_copy
Yes it does. One example is the unstable method array_assume_init in the standard library itself.
impl<T> MaybeUninit<T> {
pub unsafe fn array_assume_init<const N: usize>(array: [Self; N]) -> [T; N] {
// SAFETY:
// * The caller guarantees that all elements of the array are initialized
// * `MaybeUninit<T>` and T are guaranteed to have the same layout
// * `MaybeUninit` does not drop, so there are no double-frees
// And thus the conversion is safe
unsafe {
intrinsics::assert_inhabited::<[T; N]>();
(&array as *const _ as *const [T; N]).read()
}
}
}
(ignore the assert_inhabited thing, I don’t think it does anything important)
This doesn’t mean that your default_array method will work as intended this way. It will still misbehave if any of the T::default calls panics! (Resulting in the already-constructed values to be leaked.) But don’t worry, no need to invent something new here when the standard library has simpler (and safe) solutions…
The way to implement a default_array method, and one that actually already works on stable is to use the map method of arrays: