@@ -11,8 +11,9 @@ use crate::cmp::Ordering;
1111use crate :: convert:: { Infallible , TryFrom } ;
1212use crate :: fmt;
1313use crate :: hash:: { self , Hash } ;
14+ use crate :: iter:: TrustedLen ;
1415use crate :: marker:: Unsize ;
15- use crate :: mem:: MaybeUninit ;
16+ use crate :: mem:: { self , MaybeUninit } ;
1617use crate :: ops:: { Index , IndexMut } ;
1718use crate :: slice:: { Iter , IterMut } ;
1819
@@ -426,41 +427,13 @@ impl<T, const N: usize> [T; N] {
426427 /// assert_eq!(y, [6, 9, 3, 3]);
427428 /// ```
428429 #[ unstable( feature = "array_map" , issue = "75243" ) ]
429- pub fn map < F , U > ( self , mut f : F ) -> [ U ; N ]
430+ pub fn map < F , U > ( self , f : F ) -> [ U ; N ]
430431 where
431432 F : FnMut ( T ) -> U ,
432433 {
433- struct Guard < T , const N : usize > {
434- dst : * mut T ,
435- initialized : usize ,
436- }
437-
438- impl < T , const N : usize > Drop for Guard < T , N > {
439- fn drop ( & mut self ) {
440- debug_assert ! ( self . initialized <= N ) ;
441-
442- let initialized_part =
443- crate :: ptr:: slice_from_raw_parts_mut ( self . dst , self . initialized ) ;
444- // SAFETY: this raw slice will contain only initialized objects
445- // that's why, it is allowed to drop it.
446- unsafe {
447- crate :: ptr:: drop_in_place ( initialized_part) ;
448- }
449- }
450- }
451- let mut dst = MaybeUninit :: uninit_array :: < N > ( ) ;
452- let mut guard: Guard < U , N > =
453- Guard { dst : MaybeUninit :: slice_as_mut_ptr ( & mut dst) , initialized : 0 } ;
454- for ( src, dst) in IntoIter :: new ( self ) . zip ( & mut dst) {
455- dst. write ( f ( src) ) ;
456- guard. initialized += 1 ;
457- }
458- // FIXME: Convert to crate::mem::transmute once it works with generics.
459- // unsafe { crate::mem::transmute::<[MaybeUninit<U>; N], [U; N]>(dst) }
460- crate :: mem:: forget ( guard) ;
461- // SAFETY: At this point we've properly initialized the whole array
462- // and we just need to cast it to the correct type.
463- unsafe { crate :: mem:: transmute_copy :: < _ , [ U ; N ] > ( & dst) }
434+ // SAFETY: we know for certain that this iterator will yield exactly `N`
435+ // items.
436+ unsafe { collect_into_array_unchecked ( & mut IntoIter :: new ( self ) . map ( f) ) }
464437 }
465438
466439 /// 'Zips up' two arrays into a single array of pairs.
@@ -481,15 +454,11 @@ impl<T, const N: usize> [T; N] {
481454 /// ```
482455 #[ unstable( feature = "array_zip" , issue = "80094" ) ]
483456 pub fn zip < U > ( self , rhs : [ U ; N ] ) -> [ ( T , U ) ; N ] {
484- let mut dst = MaybeUninit :: uninit_array :: < N > ( ) ;
485- for ( i, ( lhs, rhs) ) in IntoIter :: new ( self ) . zip ( IntoIter :: new ( rhs) ) . enumerate ( ) {
486- dst[ i] . write ( ( lhs, rhs) ) ;
487- }
488- // FIXME: Convert to crate::mem::transmute once it works with generics.
489- // unsafe { crate::mem::transmute::<[MaybeUninit<U>; N], [U; N]>(dst) }
490- // SAFETY: At this point we've properly initialized the whole array
491- // and we just need to cast it to the correct type.
492- unsafe { crate :: mem:: transmute_copy :: < _ , [ ( T , U ) ; N ] > ( & dst) }
457+ let mut iter = IntoIter :: new ( self ) . zip ( IntoIter :: new ( rhs) ) ;
458+
459+ // SAFETY: we know for certain that this iterator will yield exactly `N`
460+ // items.
461+ unsafe { collect_into_array_unchecked ( & mut iter) }
493462 }
494463
495464 /// Returns a slice containing the entire array. Equivalent to `&s[..]`.
@@ -535,16 +504,9 @@ impl<T, const N: usize> [T; N] {
535504 /// ```
536505 #[ unstable( feature = "array_methods" , issue = "76118" ) ]
537506 pub fn each_ref ( & self ) -> [ & T ; N ] {
538- // Unlike in `map`, we don't need a guard here, as dropping a reference
539- // is a noop.
540- let mut out = MaybeUninit :: uninit_array :: < N > ( ) ;
541- for ( src, dst) in self . iter ( ) . zip ( & mut out) {
542- dst. write ( src) ;
543- }
544-
545- // SAFETY: All elements of `dst` are properly initialized and
546- // `MaybeUninit<T>` has the same layout as `T`, so this cast is valid.
547- unsafe { ( & mut out as * mut _ as * mut [ & T ; N ] ) . read ( ) }
507+ // SAFETY: we know for certain that this iterator will yield exactly `N`
508+ // items.
509+ unsafe { collect_into_array_unchecked ( & mut self . iter ( ) ) }
548510 }
549511
550512 /// Borrows each element mutably and returns an array of mutable references
@@ -564,15 +526,103 @@ impl<T, const N: usize> [T; N] {
564526 /// ```
565527 #[ unstable( feature = "array_methods" , issue = "76118" ) ]
566528 pub fn each_mut ( & mut self ) -> [ & mut T ; N ] {
567- // Unlike in `map`, we don't need a guard here, as dropping a reference
568- // is a noop.
569- let mut out = MaybeUninit :: uninit_array :: < N > ( ) ;
570- for ( src, dst) in self . iter_mut ( ) . zip ( & mut out) {
571- dst. write ( src) ;
529+ // SAFETY: we know for certain that this iterator will yield exactly `N`
530+ // items.
531+ unsafe { collect_into_array_unchecked ( & mut self . iter_mut ( ) ) }
532+ }
533+ }
534+
535+ /// Pulls `N` items from `iter` and returns them as an array. If the iterator
536+ /// yields fewer than `N` items, this function exhibits undefined behavior.
537+ ///
538+ /// See [`collect_into_array`] for more information.
539+ ///
540+ ///
541+ /// # Safety
542+ ///
543+ /// It is up to the caller to guarantee that `iter` yields at least `N` items.
544+ /// Violating this condition causes undefined behavior.
545+ unsafe fn collect_into_array_unchecked < I , const N : usize > ( iter : & mut I ) -> [ I :: Item ; N ]
546+ where
547+ // Note: `TrustedLen` here is somewhat of an experiment. This is just an
548+ // internal function, so feel free to remove if this bound turns out to be a
549+ // bad idea. In that case, remember to also remove the lower bound
550+ // `debug_assert!` below!
551+ I : Iterator + TrustedLen ,
552+ {
553+ debug_assert ! ( N <= iter. size_hint( ) . 1 . unwrap_or( usize :: MAX ) ) ;
554+ debug_assert ! ( N <= iter. size_hint( ) . 0 ) ;
555+
556+ match collect_into_array ( iter) {
557+ Some ( array) => array,
558+ // SAFETY: covered by the function contract.
559+ None => unsafe { crate :: hint:: unreachable_unchecked ( ) } ,
560+ }
561+ }
562+
563+ /// Pulls `N` items from `iter` and returns them as an array. If the iterator
564+ /// yields fewer than `N` items, `None` is returned and all already yielded
565+ /// items are dropped.
566+ ///
567+ /// Since the iterator is passed as mutable reference and this function calls
568+ /// `next` at most `N` times, the iterator can still be used afterwards to
569+ /// retrieve the remaining items.
570+ ///
571+ /// If `iter.next()` panicks, all items already yielded by the iterator are
572+ /// dropped.
573+ fn collect_into_array < I , const N : usize > ( iter : & mut I ) -> Option < [ I :: Item ; N ] >
574+ where
575+ I : Iterator ,
576+ {
577+ if N == 0 {
578+ // SAFETY: An empty array is always inhabited and has no validity invariants.
579+ return unsafe { Some ( mem:: zeroed ( ) ) } ;
580+ }
581+
582+ struct Guard < T , const N : usize > {
583+ ptr : * mut T ,
584+ initialized : usize ,
585+ }
586+
587+ impl < T , const N : usize > Drop for Guard < T , N > {
588+ fn drop ( & mut self ) {
589+ debug_assert ! ( self . initialized <= N ) ;
590+
591+ let initialized_part = crate :: ptr:: slice_from_raw_parts_mut ( self . ptr , self . initialized ) ;
592+
593+ // SAFETY: this raw slice will contain only initialized objects.
594+ unsafe {
595+ crate :: ptr:: drop_in_place ( initialized_part) ;
596+ }
597+ }
598+ }
599+
600+ let mut array = MaybeUninit :: uninit_array :: < N > ( ) ;
601+ let mut guard: Guard < _ , N > =
602+ Guard { ptr : MaybeUninit :: slice_as_mut_ptr ( & mut array) , initialized : 0 } ;
603+
604+ while let Some ( item) = iter. next ( ) {
605+ // SAFETY: `guard.initialized` starts at 0, is increased by one in the
606+ // loop and the loop is aborted once it reaches N (which is
607+ // `array.len()`).
608+ unsafe {
609+ array. get_unchecked_mut ( guard. initialized ) . write ( item) ;
572610 }
611+ guard. initialized += 1 ;
612+
613+ // Check if the whole array was initialized.
614+ if guard. initialized == N {
615+ mem:: forget ( guard) ;
573616
574- // SAFETY: All elements of `dst` are properly initialized and
575- // `MaybeUninit<T>` has the same layout as `T`, so this cast is valid.
576- unsafe { ( & mut out as * mut _ as * mut [ & mut T ; N ] ) . read ( ) }
617+ // SAFETY: the condition above asserts that all elements are
618+ // initialized.
619+ let out = unsafe { MaybeUninit :: array_assume_init ( array) } ;
620+ return Some ( out) ;
621+ }
577622 }
623+
624+ // This is only reached if the iterator is exhausted before
625+ // `guard.initialized` reaches `N`. Also note that `guard` is dropped here,
626+ // dropping all already initialized elements.
627+ None
578628}
0 commit comments