@@ -429,7 +429,68 @@ impl<T, const N: usize> [T; N] {
429
429
where
430
430
F : FnMut ( T ) -> U ,
431
431
{
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
+
433
494
struct Guard < T , const N : usize > {
434
495
dst : * mut T ,
435
496
initialized : usize ,
@@ -457,10 +518,10 @@ impl<T, const N: usize> [T; N] {
457
518
}
458
519
// FIXME: Convert to crate::mem::transmute once it works with generics.
459
520
// unsafe { crate::mem::transmute::<[MaybeUninit<U>; N], [U; N]>(dst) }
460
- crate :: mem :: forget ( guard) ;
521
+ forget ( guard) ;
461
522
// SAFETY: At this point we've properly initialized the whole array
462
523
// 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) }
464
525
}
465
526
466
527
/// Returns a slice containing the entire array. Equivalent to `&s[..]`.
0 commit comments