Skip to content

Commit babfdaf

Browse files
authored
Rollup merge of #80600 - CoffeeBlend:maybe_uninit_array_assume_init, r=dtolnay
Add `MaybeUninit` method `array_assume_init` When initialising an array element-by-element, the conversion to the initialised array is done through `mem::transmute`, which is both ugly and does not work with const generics (see #61956). This PR proposes the associated method `array_assume_init`, matching the style of `slice_assume_init_*`: ```rust unsafe fn array_assume_init<T, const N: usize>(array: [MaybeUninit<T>; N]) -> [T; N]; ``` Example: ```rust let mut array: [MaybeUninit<i32>; 3] = MaybeUninit::uninit_array(); array[0].write(0); array[1].write(1); array[2].write(2); // SAFETY: Now safe as we initialised all elements let array: [i32; 3] = unsafe { MaybeUninit::array_assume_init(array) }; ``` Things I'm unsure about: * Should this be a method of array instead? * Should the function be const?
2 parents 86b900a + 985071b commit babfdaf

File tree

3 files changed

+56
-0
lines changed

3 files changed

+56
-0
lines changed

library/core/src/mem/maybe_uninit.rs

+40
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,46 @@ impl<T> MaybeUninit<T> {
804804
}
805805
}
806806

807+
/// Extracts the values from an array of `MaybeUninit` containers.
808+
///
809+
/// # Safety
810+
///
811+
/// It is up to the caller to guarantee that all elements of the array are
812+
/// in an initialized state.
813+
///
814+
/// # Examples
815+
///
816+
/// ```
817+
/// #![feature(maybe_uninit_uninit_array)]
818+
/// #![feature(maybe_uninit_array_assume_init)]
819+
/// use std::mem::MaybeUninit;
820+
///
821+
/// let mut array: [MaybeUninit<i32>; 3] = MaybeUninit::uninit_array();
822+
/// array[0] = MaybeUninit::new(0);
823+
/// array[1] = MaybeUninit::new(1);
824+
/// array[2] = MaybeUninit::new(2);
825+
///
826+
/// // SAFETY: Now safe as we initialised all elements
827+
/// let array = unsafe {
828+
/// MaybeUninit::array_assume_init(array)
829+
/// };
830+
///
831+
/// assert_eq!(array, [0, 1, 2]);
832+
/// ```
833+
#[unstable(feature = "maybe_uninit_array_assume_init", issue = "80908")]
834+
#[inline(always)]
835+
pub unsafe fn array_assume_init<const N: usize>(array: [Self; N]) -> [T; N] {
836+
// SAFETY:
837+
// * The caller guarantees that all elements of the array are initialized
838+
// * `MaybeUninit<T>` and T are guaranteed to have the same layout
839+
// * MaybeUnint does not drop, so there are no double-frees
840+
// And thus the conversion is safe
841+
unsafe {
842+
intrinsics::assert_inhabited::<T>();
843+
(&array as *const _ as *const [T; N]).read()
844+
}
845+
}
846+
807847
/// Assuming all the elements are initialized, get a slice to them.
808848
///
809849
/// # Safety

library/core/tests/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
#![feature(raw)]
3737
#![feature(sort_internals)]
3838
#![feature(slice_partition_at_index)]
39+
#![feature(maybe_uninit_uninit_array)]
40+
#![feature(maybe_uninit_array_assume_init)]
3941
#![feature(maybe_uninit_extra)]
4042
#![feature(maybe_uninit_write_slice)]
4143
#![feature(min_specialization)]

library/core/tests/mem.rs

+14
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,20 @@ fn assume_init_good() {
140140
assert!(TRUE);
141141
}
142142

143+
#[test]
144+
fn uninit_array_assume_init() {
145+
let mut array: [MaybeUninit<i16>; 5] = MaybeUninit::uninit_array();
146+
array[0].write(3);
147+
array[1].write(1);
148+
array[2].write(4);
149+
array[3].write(1);
150+
array[4].write(5);
151+
152+
let array = unsafe { MaybeUninit::array_assume_init(array) };
153+
154+
assert_eq!(array, [3, 1, 4, 1, 5]);
155+
}
156+
143157
#[test]
144158
fn uninit_write_slice() {
145159
let mut dst = [MaybeUninit::new(255); 64];

0 commit comments

Comments
 (0)