about summary refs log tree commit diff
diff options
context:
space:
mode:
authorYuki Okushi <huyuumi.dev@gmail.com>2021-01-12 16:13:24 +0900
committerGitHub <noreply@github.com>2021-01-12 16:13:24 +0900
commitbabfdafb10cb77f980b11eb7e2ccf0ccdb72b8a1 (patch)
tree4f0a9446173ddd9fe94683fc4d1ed2edcd516701
parent86b900a3eaaf84b48f080dab14b7994385bd19b8 (diff)
parent985071b08f5c03e4f18d43c15f3ea82395588a5e (diff)
downloadrust-babfdafb10cb77f980b11eb7e2ccf0ccdb72b8a1.tar.gz
rust-babfdafb10cb77f980b11eb7e2ccf0ccdb72b8a1.zip
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?
-rw-r--r--library/core/src/mem/maybe_uninit.rs40
-rw-r--r--library/core/tests/lib.rs2
-rw-r--r--library/core/tests/mem.rs14
3 files changed, 56 insertions, 0 deletions
diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs
index b2a4d897eed..fda0553f94c 100644
--- a/library/core/src/mem/maybe_uninit.rs
+++ b/library/core/src/mem/maybe_uninit.rs
@@ -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
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index e01aaa4cbf1..bc737cd1927 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -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)]
diff --git a/library/core/tests/mem.rs b/library/core/tests/mem.rs
index 79ca2bba403..2279a16429f 100644
--- a/library/core/tests/mem.rs
+++ b/library/core/tests/mem.rs
@@ -141,6 +141,20 @@ fn assume_init_good() {
 }
 
 #[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];
     let src = [0; 64];