@@ -429,7 +429,68 @@ impl<T, const N: usize> [T; N] {
429429 where
430430 F : FnMut ( T ) -> U ,
431431 {
432- use crate :: mem:: MaybeUninit ;
432+ use crate :: mem:: { align_of, forget, size_of, transmute_copy, ManuallyDrop , MaybeUninit } ;
433+
434+ if align_of :: < T > ( ) == align_of :: < U > ( ) && size_of :: < T > ( ) == size_of :: < U > ( ) {
435+ // this branch allows reuse of the original array as a backing store
436+ // kind of. As written with no compiler optimizations, transmute copy will
437+ // still require 2 copies of the original array, but when it can be converted to
438+ // transmute, this will require 0 copies.
439+ union Translated < T , U > {
440+ src : MaybeUninit < T > ,
441+ dst : ManuallyDrop < U > ,
442+ } ;
443+ struct Guard < T , U , const N : usize > {
444+ data : * mut [ Translated < T , U > ; N ] ,
445+ initialized : usize ,
446+ }
447+ impl < T , U , const N : usize > Drop for Guard < T , U , N > {
448+ fn drop ( & mut self ) {
449+ debug_assert ! ( self . initialized < N ) ;
450+ let initialized_part =
451+ crate :: ptr:: slice_from_raw_parts_mut ( self . data as * mut U , self . initialized ) ;
452+ // SAFETY:
453+ // since we read from the element at initialized then panicked,
454+ // we have to skip over it to not double drop.
455+ let todo_ptr = unsafe { self . data . add ( self . initialized + 1 ) as * mut T } ;
456+ let todo_part =
457+ crate :: ptr:: slice_from_raw_parts_mut ( todo_ptr, N - self . initialized - 1 ) ;
458+ // SAFETY:
459+ // Have to remove both the initialized and not yet reached items.
460+ unsafe {
461+ crate :: ptr:: drop_in_place ( initialized_part) ;
462+ crate :: ptr:: drop_in_place ( todo_part) ;
463+ }
464+ }
465+ }
466+ // SAFETY:
467+ // Since we know that T & U have the same size and alignment we can safely transmute
468+ // between them here
469+ let mut src_dst = unsafe { transmute_copy :: < _ , [ Translated < T , U > ; N ] > ( & self ) } ;
470+
471+ let mut guard: Guard < T , U , N > = Guard { data : & mut src_dst, initialized : 0 } ;
472+ // Need to forget self now because the guard is responsible for dropping the items
473+ forget ( self ) ;
474+ for i in 0 ..N {
475+ // SAFETY:
476+ // All items prior to `i` are the `dst` variant.
477+ // In order to convert `i` from src to dst, we take it from `MaybeUninit`,
478+ // leaving uninitialized in its place, and set the destination as
479+ // ManuallyDrop::new(..), and implicitly know that it will be a `dst` variant
480+ // from where
481+ unsafe {
482+ let v = f ( src_dst[ i] . src . read ( ) ) ;
483+ src_dst[ i] . dst = ManuallyDrop :: new ( v) ;
484+ }
485+ guard. initialized += 1 ;
486+ }
487+ forget ( guard) ;
488+ // SAFETY:
489+ // At this point all the items have been initialized and are in `dst` discriminant.
490+ // We can switch them over to being of type `U`.
491+ return unsafe { transmute_copy :: < _ , [ U ; N ] > ( & src_dst) } ;
492+ }
493+
433494 struct Guard < T , const N : usize > {
434495 dst : * mut T ,
435496 initialized : usize ,
@@ -457,10 +518,10 @@ impl<T, const N: usize> [T; N] {
457518 }
458519 // FIXME: Convert to crate::mem::transmute once it works with generics.
459520 // unsafe { crate::mem::transmute::<[MaybeUninit<U>; N], [U; N]>(dst) }
460- crate :: mem :: forget ( guard) ;
521+ forget ( guard) ;
461522 // SAFETY: At this point we've properly initialized the whole array
462523 // and we just need to cast it to the correct type.
463- unsafe { crate :: mem :: transmute_copy :: < _ , [ U ; N ] > ( & dst) }
524+ unsafe { transmute_copy :: < _ , [ U ; N ] > ( & dst) }
464525 }
465526
466527 /// Returns a slice containing the entire array. Equivalent to `&s[..]`.
0 commit comments