Skip to content

Commit

Permalink
Rollup merge of #80600 - CoffeeBlend:maybe_uninit_array_assume_init, …
Browse files Browse the repository at this point in the history
…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?
  • Loading branch information
JohnTitor authored Jan 12, 2021
2 parents 86b900a + 985071b commit babfdaf
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 0 deletions.
40 changes: 40 additions & 0 deletions library/core/src/mem/maybe_uninit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,46 @@ impl<T> MaybeUninit<T> {
}
}

/// Extracts the values from an array of `MaybeUninit` containers.
///
/// # Safety
///
/// It is up to the caller to guarantee that all elements of the array are
/// in an initialized state.
///
/// # Examples
///
/// ```
/// #![feature(maybe_uninit_uninit_array)]
/// #![feature(maybe_uninit_array_assume_init)]
/// use std::mem::MaybeUninit;
///
/// let mut array: [MaybeUninit<i32>; 3] = MaybeUninit::uninit_array();
/// array[0] = MaybeUninit::new(0);
/// array[1] = MaybeUninit::new(1);
/// array[2] = MaybeUninit::new(2);
///
/// // SAFETY: Now safe as we initialised all elements
/// let array = unsafe {
/// MaybeUninit::array_assume_init(array)
/// };
///
/// assert_eq!(array, [0, 1, 2]);
/// ```
#[unstable(feature = "maybe_uninit_array_assume_init", issue = "80908")]
#[inline(always)]
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
// * MaybeUnint does not drop, so there are no double-frees
// And thus the conversion is safe
unsafe {
intrinsics::assert_inhabited::<T>();
(&array as *const _ as *const [T; N]).read()
}
}

/// Assuming all the elements are initialized, get a slice to them.
///
/// # Safety
Expand Down
2 changes: 2 additions & 0 deletions library/core/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
#![feature(raw)]
#![feature(sort_internals)]
#![feature(slice_partition_at_index)]
#![feature(maybe_uninit_uninit_array)]
#![feature(maybe_uninit_array_assume_init)]
#![feature(maybe_uninit_extra)]
#![feature(maybe_uninit_write_slice)]
#![feature(min_specialization)]
Expand Down
14 changes: 14 additions & 0 deletions library/core/tests/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,20 @@ fn assume_init_good() {
assert!(TRUE);
}

#[test]
fn uninit_array_assume_init() {
let mut array: [MaybeUninit<i16>; 5] = MaybeUninit::uninit_array();
array[0].write(3);
array[1].write(1);
array[2].write(4);
array[3].write(1);
array[4].write(5);

let array = unsafe { MaybeUninit::array_assume_init(array) };

assert_eq!(array, [3, 1, 4, 1, 5]);
}

#[test]
fn uninit_write_slice() {
let mut dst = [MaybeUninit::new(255); 64];
Expand Down

0 comments on commit babfdaf

Please sign in to comment.