@@ -825,14 +825,13 @@ impl<T, const N: usize> [T; N] {
825825/// Pulls `N` items from `iter` and returns them as an array. If the iterator
826826/// yields fewer than `N` items, this function exhibits undefined behavior.
827827///
828- /// See [`try_collect_into_array`] for more information.
829- ///
830- ///
831828/// # Safety
832829///
833830/// It is up to the caller to guarantee that `iter` yields at least `N` items.
834831/// Violating this condition causes undefined behavior.
835- unsafe fn try_collect_into_array_unchecked < I , T , R , const N : usize > ( iter : & mut I ) -> R :: TryType
832+ unsafe fn try_collect_into_array_unchecked < I , T , R , const N : usize > (
833+ iter : & mut I ,
834+ ) -> ChangeOutputType < I :: Item , [ T ; N ] >
836835where
837836 // Note: `TrustedLen` here is somewhat of an experiment. This is just an
838837 // internal function, so feel free to remove if this bound turns out to be a
@@ -845,11 +844,21 @@ where
845844 debug_assert ! ( N <= iter. size_hint( ) . 1 . unwrap_or( usize :: MAX ) ) ;
846845 debug_assert ! ( N <= iter. size_hint( ) . 0 ) ;
847846
848- // SAFETY: covered by the function contract.
849- unsafe { try_collect_into_array ( iter) . unwrap_unchecked ( ) }
847+ let mut array = MaybeUninit :: uninit_array :: < N > ( ) ;
848+ let cf = try_collect_into_array_erased ( iter, & mut array) ;
849+ match cf {
850+ ControlFlow :: Break ( r) => FromResidual :: from_residual ( r) ,
851+ ControlFlow :: Continue ( initialized) => {
852+ debug_assert_eq ! ( initialized, N ) ;
853+ // SAFETY: because of our function contract, all the elements
854+ // must have been initialized.
855+ let output = unsafe { MaybeUninit :: array_assume_init ( array) } ;
856+ Try :: from_output ( output)
857+ }
858+ }
850859}
851860
852- // Infallible version of `try_collect_into_array_unchecked`.
861+ /// Infallible version of [ `try_collect_into_array_unchecked`] .
853862unsafe fn collect_into_array_unchecked < I , const N : usize > ( iter : & mut I ) -> [ I :: Item ; N ]
854863where
855864 I : Iterator + TrustedLen ,
@@ -864,63 +873,48 @@ where
864873 }
865874}
866875
867- /// Pulls `N` items from `iter` and returns them as an array. If the iterator
868- /// yields fewer than `N` items, `Err` is returned containing an iterator over
869- /// the already yielded items.
876+ /// Rather than *returning* the array, this fills in a passed-in buffer.
877+ /// If any of the iterator elements short-circuit, it drops everything in the
878+ /// buffer and return the error. Otherwise it returns the number of items
879+ /// which were initialized in the buffer.
870880///
871- /// Since the iterator is passed as a mutable reference and this function calls
872- /// `next` at most `N` times, the iterator can still be used afterwards to
873- /// retrieve the remaining items.
881+ /// (The caller is responsible for dropping those items on success, but not
882+ /// doing that is just a leak, not UB, so this function is itself safe.)
874883///
875- /// If `iter.next()` panicks, all items already yielded by the iterator are
876- /// dropped.
884+ /// This means less monomorphization, but more importantly it means that the
885+ /// returned array doesn't need to be copied into the `Result`, since returning
886+ /// the result seemed (2023-01) to cause in an extra `N + 1`-length `alloca`
887+ /// even if it's always `unwrap_unchecked` later.
877888#[ inline]
878- fn try_collect_into_array < I , T , R , const N : usize > (
889+ fn try_collect_into_array_erased < I , T , R > (
879890 iter : & mut I ,
880- ) -> Result < R :: TryType , IntoIter < T , N > >
891+ buffer : & mut [ MaybeUninit < T > ] ,
892+ ) -> ControlFlow < R , usize >
881893where
882894 I : Iterator ,
883895 I :: Item : Try < Output = T , Residual = R > ,
884- R : Residual < [ T ; N ] > ,
885896{
886- if N == 0 {
887- // SAFETY: An empty array is always inhabited and has no validity invariants.
888- return Ok ( Try :: from_output ( unsafe { mem:: zeroed ( ) } ) ) ;
889- }
890-
891- let mut array = MaybeUninit :: uninit_array :: < N > ( ) ;
892- let mut guard = Guard { array_mut : & mut array, initialized : 0 } ;
897+ let n = buffer. len ( ) ;
898+ let mut guard = Guard { array_mut : buffer, initialized : 0 } ;
893899
894- for _ in 0 ..N {
900+ for _ in 0 ..n {
895901 match iter. next ( ) {
896902 Some ( item_rslt) => {
897- let item = match item_rslt. branch ( ) {
898- ControlFlow :: Break ( r) => {
899- return Ok ( FromResidual :: from_residual ( r) ) ;
900- }
901- ControlFlow :: Continue ( elem) => elem,
902- } ;
903+ let item = item_rslt. branch ( ) ?;
903904
904905 // SAFETY: `guard.initialized` starts at 0, which means push can be called
905- // at most N times, which this loop does.
906+ // at most `n` times, which this loop does.
906907 unsafe {
907908 guard. push_unchecked ( item) ;
908909 }
909910 }
910- None => {
911- let alive = 0 ..guard. initialized ;
912- mem:: forget ( guard) ;
913- // SAFETY: `array` was initialized with exactly `initialized`
914- // number of elements.
915- return Err ( unsafe { IntoIter :: new_unchecked ( array, alive) } ) ;
916- }
911+ None => break ,
917912 }
918913 }
919914
915+ let initialized = guard. initialized ;
920916 mem:: forget ( guard) ;
921- // SAFETY: All elements of the array were populated in the loop above.
922- let output = unsafe { array. transpose ( ) . assume_init ( ) } ;
923- Ok ( Try :: from_output ( output) )
917+ ControlFlow :: Continue ( initialized)
924918}
925919
926920/// Panic guard for incremental initialization of arrays.
@@ -934,14 +928,14 @@ where
934928///
935929/// To minimize indirection fields are still pub but callers should at least use
936930/// `push_unchecked` to signal that something unsafe is going on.
937- pub ( crate ) struct Guard < ' a , T , const N : usize > {
931+ pub ( crate ) struct Guard < ' a , T > {
938932 /// The array to be initialized.
939- pub array_mut : & ' a mut [ MaybeUninit < T > ; N ] ,
933+ pub array_mut : & ' a mut [ MaybeUninit < T > ] ,
940934 /// The number of items that have been initialized so far.
941935 pub initialized : usize ,
942936}
943937
944- impl < T , const N : usize > Guard < ' _ , T , N > {
938+ impl < T > Guard < ' _ , T > {
945939 /// Adds an item to the array and updates the initialized item counter.
946940 ///
947941 /// # Safety
@@ -959,9 +953,9 @@ impl<T, const N: usize> Guard<'_, T, N> {
959953 }
960954}
961955
962- impl < T , const N : usize > Drop for Guard < ' _ , T , N > {
956+ impl < T > Drop for Guard < ' _ , T > {
963957 fn drop ( & mut self ) {
964- debug_assert ! ( self . initialized <= N ) ;
958+ debug_assert ! ( self . initialized <= self . array_mut . len ( ) ) ;
965959
966960 // SAFETY: this slice will contain only initialized objects.
967961 unsafe {
@@ -972,15 +966,33 @@ impl<T, const N: usize> Drop for Guard<'_, T, N> {
972966 }
973967}
974968
975- /// Returns the next chunk of `N` items from the iterator or errors with an
976- /// iterator over the remainder. Used for `Iterator::next_chunk`.
969+ /// Pulls `N` items from `iter` and returns them as an array. If the iterator
970+ /// yields fewer than `N` items, `Err` is returned containing an iterator over
971+ /// the already yielded items.
972+ ///
973+ /// Since the iterator is passed as a mutable reference and this function calls
974+ /// `next` at most `N` times, the iterator can still be used afterwards to
975+ /// retrieve the remaining items.
976+ ///
977+ /// If `iter.next()` panicks, all items already yielded by the iterator are
978+ /// dropped.
979+ ///
980+ /// Used for [`Iterator::next_chunk`].
977981#[ inline]
978- pub ( crate ) fn iter_next_chunk < I , const N : usize > (
979- iter : & mut I ,
980- ) -> Result < [ I :: Item ; N ] , IntoIter < I :: Item , N > >
981- where
982- I : Iterator ,
983- {
982+ pub ( crate ) fn iter_next_chunk < T , const N : usize > (
983+ iter : & mut impl Iterator < Item = T > ,
984+ ) -> Result < [ T ; N ] , IntoIter < T , N > > {
984985 let mut map = iter. map ( NeverShortCircuit ) ;
985- try_collect_into_array ( & mut map) . map ( |NeverShortCircuit ( arr) | arr)
986+ let mut array = MaybeUninit :: uninit_array :: < N > ( ) ;
987+ let ControlFlow :: Continue ( initialized) = try_collect_into_array_erased ( & mut map, & mut array) ;
988+ if initialized == N {
989+ // SAFETY: All elements of the array were populated.
990+ let output = unsafe { MaybeUninit :: array_assume_init ( array) } ;
991+ Ok ( output)
992+ } else {
993+ let alive = 0 ..initialized;
994+ // SAFETY: `array` was initialized with exactly `initialized`
995+ // number of elements.
996+ return Err ( unsafe { IntoIter :: new_unchecked ( array, alive) } ) ;
997+ }
986998}
0 commit comments