From 355d5e8413e2bc58a96698177090efe1407bf647 Mon Sep 17 00:00:00 2001 From: incertia Date: Tue, 10 Nov 2026 16:58:12 -0500 Subject: [PATCH 1/3] promote Array::assume_init to const --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 76ada8a..644246f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -557,7 +557,7 @@ where /// It is up to the caller to guarantee that all elements of the array are in an initialized /// state. #[inline] - pub unsafe fn assume_init(self) -> Array { + pub const unsafe fn assume_init(self) -> Array { unsafe { // `Array` is a `repr(transparent)` newtype for a generic inner type which is constrained to // be `[T; N]` by the `ArraySize` impls in this crate. @@ -570,7 +570,7 @@ where // don't need to worry about `Drop` impls because `MaybeUninit` does not impl `Drop`. // Since we have unique ownership of `self`, it's okay to make a copy because we're throwing // the original away (and this should all get optimized to a noop by the compiler, anyway). - mem::transmute_copy(&self) + mem::transmute_copy(&ManuallyDrop::new(self)) } } } From d363146af3da7e842abe619c5d4a44559c10edc1 Mon Sep 17 00:00:00 2001 From: incertia Date: Tue, 10 Nov 2026 18:42:12 -0500 Subject: [PATCH 2/3] update comment regarding transmute safety and Drop --- src/lib.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 644246f..c303777 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -560,14 +560,23 @@ where pub const unsafe fn assume_init(self) -> Array { unsafe { // `Array` is a `repr(transparent)` newtype for a generic inner type which is constrained to - // be `[T; N]` by the `ArraySize` impls in this crate. + // be `[T; N]` by the `ArraySize` impls in this crate. However, this is merely a SAFETY + // guideline and not checkable by the compiler. Therefore, the compiler's inability to verify + // that the sizes of `U::ArrayType` and `U::ArrayType>` does not allow us + // to use `transmute`. // - // Since we're working with a type-erased inner type and ultimately trying to convert - // `[MaybeUninit; N]` to `[T; N]`, we can't use simpler approaches like a pointer cast - // or `transmute`, since the compiler can't prove to itself that the size will be the same. + // As such, we use `transmute_copy`, which reinterprets `&U::ArrayType>` as + // `&U::ArrayType', which functionally means `&[MaybeUninit; N]` as `&[T; N]` and then + // dereferences the result to obtain a copy. Because of this, `transmute_copy(&self)` does + // not move out of `self` and the destructor for U::ArrayType> still runs when + // dropping `self`. + // + // To resolve the issue of `self` still being dropped, we first move `self` into + // `ManuallyDrop`, which is also `repr(transparent)`. We then create a reference, which is + // transmuted into a copy (with live destructors), and then implicitly drop the + // `ManuallyDrop`, not running any destructors, as the data is now managed by the copy that + // was just produced. // - // We've taken unique ownership of `self`, which is a `MaybeUninit` array, and as such we - // don't need to worry about `Drop` impls because `MaybeUninit` does not impl `Drop`. // Since we have unique ownership of `self`, it's okay to make a copy because we're throwing // the original away (and this should all get optimized to a noop by the compiler, anyway). mem::transmute_copy(&ManuallyDrop::new(self)) From d52656f12e2ad5b57f92dafcc65d6e487ad3143f Mon Sep 17 00:00:00 2001 From: incertia Date: Tue, 10 Nov 2026 21:27:06 -0500 Subject: [PATCH 3/3] use mem::forget --- src/lib.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c303777..b5baef7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -567,19 +567,18 @@ where // // As such, we use `transmute_copy`, which reinterprets `&U::ArrayType>` as // `&U::ArrayType', which functionally means `&[MaybeUninit; N]` as `&[T; N]` and then - // dereferences the result to obtain a copy. Because of this, `transmute_copy(&self)` does - // not move out of `self` and the destructor for U::ArrayType> still runs when - // dropping `self`. - // - // To resolve the issue of `self` still being dropped, we first move `self` into - // `ManuallyDrop`, which is also `repr(transparent)`. We then create a reference, which is - // transmuted into a copy (with live destructors), and then implicitly drop the - // `ManuallyDrop`, not running any destructors, as the data is now managed by the copy that - // was just produced. + // dereferences the result to obtain a copy. // // Since we have unique ownership of `self`, it's okay to make a copy because we're throwing // the original away (and this should all get optimized to a noop by the compiler, anyway). - mem::transmute_copy(&ManuallyDrop::new(self)) + let result = mem::transmute_copy(&self); + + // At this point, we have two copies of the data, in `self` and `result`, since + // `transmute_copy` does not move out of `self`. The compiler is unable to deduce that all + // possible destructors for `self` are trivial and effectively noops, so we manually *forget* + // it by calling `mem::forget`. This is the "throwing away" mentioned in the above comment. + mem::forget(self); + result } } }