@@ -964,16 +964,46 @@ impl<T: [const] Destruct> const Drop for Guard<'_, T> {
964964 }
965965}
966966
967- /// Pulls `N` items from `iter` and returns them as an array. If the iterator
968- /// yields fewer than `N ` items, `Err ` is returned containing an iterator over
969- /// the already yielded items.
970- ///
971- /// Since the iterator is passed as a mutable reference and this function calls
972- /// `next` at most `N ` times, the iterator can still be used afterwards to
973- /// retrieve the remaining items.
974- ///
975- /// If `iter.next()` panics, all items already yielded by the iterator are
976- /// dropped.
967+ /// Panic guard for incremental initialization of arrays from the back.
968+ struct GuardBack <' a, T > {
969+ /// The array to be initialized.
970+ pub array_mut: & ' a mut [ MaybeUninit <T >] ,
971+ /// The number of items that have been initialized so far.
972+ pub initialized: usize,
973+ }
974+
975+ impl<T > GuardBack <' _, T > {
976+ /// Adds an item to the array and updates the initialized item counter.
977+ ///
978+ /// # Safety
979+ ///
980+ /// No more than N elements must be initialized.
981+ #[ inline]
982+ pub ( crate ) unsafe fn push_unchecked ( & mut self , item : T ) {
983+ // SAFETY: If `initialized` was correct before and the caller does not
984+ // invoke this method more than N times, then writes will be in-bounds
985+ // and slots will not be initialized more than once.
986+ unsafe {
987+ self . initialized = self . initialized . unchecked_add ( 1 ) ;
988+ let index = self . array_mut . len ( ) . unchecked_sub ( self . initialized ) ;
989+ self . array_mut . get_unchecked_mut ( index) . write ( item) ;
990+ }
991+ }
992+ }
993+
994+ impl < T : Destruct > Drop for GuardBack < ' _ , T > {
995+ #[ inline]
996+ fn drop ( & mut self ) {
997+ debug_assert ! ( self . initialized <= self . array_mut. len( ) ) ;
998+ let len = self . array_mut . len ( ) ;
999+ // SAFETY: this slice will contain only initialized objects.
1000+ unsafe {
1001+ self . array_mut . get_unchecked_mut ( len - self . initialized ..len) . assume_init_drop ( ) ;
1002+ }
1003+ }
1004+ }
1005+
1006+ /// Pulls `N` items from `iter`.
9771007///
9781008/// Used for [`Iterator::next_chunk`].
9791009#[ inline]
@@ -994,11 +1024,7 @@ pub(crate) fn iter_next_chunk<T, const N: usize>(
9941024 }
9951025}
9961026
997- /// Version of [`iter_next_chunk`] using a passed-in slice in order to avoid
998- /// needing to monomorphize for every array length.
999- ///
1000- /// Unfortunately this loop has two exit conditions, the buffer filling up
1001- /// or the iterator running out of items, making it tend to optimize poorly.
1027+ /// Version of [`iter_next_chunk`] using a passed-in slice.
10021028#[ inline]
10031029fn iter_next_chunk_erased < T > (
10041030 buffer : & mut [ MaybeUninit < T > ] ,
@@ -1022,3 +1048,47 @@ fn iter_next_chunk_erased<T>(
10221048 mem:: forget ( guard) ;
10231049 Ok ( ( ) )
10241050}
1051+
1052+ /// Pulls `N` items from the back of `iter`.
1053+ ///
1054+ /// Used for [`DoubleEndedIterator::next_chunk_back`].
1055+ #[ inline]
1056+ pub ( crate ) fn iter_next_chunk_back < T , const N : usize > (
1057+ iter : & mut impl DoubleEndedIterator < Item = T > ,
1058+ ) -> Result < [ T ; N ] , IntoIter < T , N > > {
1059+ let mut array = [ const { MaybeUninit :: uninit ( ) } ; N ] ;
1060+ let r = iter_next_chunk_back_erased ( & mut array, iter) ;
1061+ match r {
1062+ Ok ( ( ) ) => {
1063+ // SAFETY: All elements of `array` were populated.
1064+ Ok ( unsafe { MaybeUninit :: array_assume_init ( array) } )
1065+ }
1066+ Err ( initialized) => {
1067+ // SAFETY: Only the last `initialized` elements were populated
1068+ Err ( unsafe { IntoIter :: new_unchecked ( array, N - initialized..N ) } )
1069+ }
1070+ }
1071+ }
1072+
1073+ /// Version of [`iter_next_chunk_back`] using a passed-in slice.
1074+ #[ inline]
1075+ fn iter_next_chunk_back_erased < T > (
1076+ buffer : & mut [ MaybeUninit < T > ] ,
1077+ iter : & mut impl DoubleEndedIterator < Item = T > ,
1078+ ) -> Result < ( ) , usize > {
1079+ // if `Iterator::next_back` panics, this guard will drop already initialized items
1080+ let mut guard = GuardBack { array_mut : buffer, initialized : 0 } ;
1081+ while guard. initialized < guard. array_mut . len ( ) {
1082+ let Some ( item) = iter. next_back ( ) else {
1083+ let initialized = guard. initialized ;
1084+ mem:: forget ( guard) ;
1085+ return Err ( initialized) ;
1086+ } ;
1087+
1088+ // SAFETY: The loop condition ensures we have space to push the item
1089+ unsafe { guard. push_unchecked ( item) } ;
1090+ }
1091+
1092+ mem:: forget ( guard) ;
1093+ Ok ( ( ) )
1094+ }
0 commit comments