@@ -825,14 +825,13 @@ impl<T, const N: usize> [T; N] {
825
825
/// Pulls `N` items from `iter` and returns them as an array. If the iterator
826
826
/// yields fewer than `N` items, this function exhibits undefined behavior.
827
827
///
828
- /// See [`try_collect_into_array`] for more information.
829
- ///
830
- ///
831
828
/// # Safety
832
829
///
833
830
/// It is up to the caller to guarantee that `iter` yields at least `N` items.
834
831
/// 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 ] >
836
835
where
837
836
// Note: `TrustedLen` here is somewhat of an experiment. This is just an
838
837
// internal function, so feel free to remove if this bound turns out to be a
@@ -845,11 +844,21 @@ where
845
844
debug_assert ! ( N <= iter. size_hint( ) . 1 . unwrap_or( usize :: MAX ) ) ;
846
845
debug_assert ! ( N <= iter. size_hint( ) . 0 ) ;
847
846
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
+ }
850
859
}
851
860
852
- // Infallible version of `try_collect_into_array_unchecked`.
861
+ /// Infallible version of [ `try_collect_into_array_unchecked`] .
853
862
unsafe fn collect_into_array_unchecked < I , const N : usize > ( iter : & mut I ) -> [ I :: Item ; N ]
854
863
where
855
864
I : Iterator + TrustedLen ,
@@ -864,63 +873,48 @@ where
864
873
}
865
874
}
866
875
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.
870
880
///
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.)
874
883
///
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.
877
888
#[ inline]
878
- fn try_collect_into_array < I , T , R , const N : usize > (
889
+ fn try_collect_into_array_erased < I , T , R > (
879
890
iter : & mut I ,
880
- ) -> Result < R :: TryType , IntoIter < T , N > >
891
+ buffer : & mut [ MaybeUninit < T > ] ,
892
+ ) -> ControlFlow < R , usize >
881
893
where
882
894
I : Iterator ,
883
895
I :: Item : Try < Output = T , Residual = R > ,
884
- R : Residual < [ T ; N ] > ,
885
896
{
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 } ;
893
899
894
- for _ in 0 ..N {
900
+ for _ in 0 ..n {
895
901
match iter. next ( ) {
896
902
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 ( ) ?;
903
904
904
905
// 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.
906
907
unsafe {
907
908
guard. push_unchecked ( item) ;
908
909
}
909
910
}
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 ,
917
912
}
918
913
}
919
914
915
+ let initialized = guard. initialized ;
920
916
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)
924
918
}
925
919
926
920
/// Panic guard for incremental initialization of arrays.
@@ -934,14 +928,14 @@ where
934
928
///
935
929
/// To minimize indirection fields are still pub but callers should at least use
936
930
/// `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 > {
938
932
/// The array to be initialized.
939
- pub array_mut : & ' a mut [ MaybeUninit < T > ; N ] ,
933
+ pub array_mut : & ' a mut [ MaybeUninit < T > ] ,
940
934
/// The number of items that have been initialized so far.
941
935
pub initialized : usize ,
942
936
}
943
937
944
- impl < T , const N : usize > Guard < ' _ , T , N > {
938
+ impl < T > Guard < ' _ , T > {
945
939
/// Adds an item to the array and updates the initialized item counter.
946
940
///
947
941
/// # Safety
@@ -959,9 +953,9 @@ impl<T, const N: usize> Guard<'_, T, N> {
959
953
}
960
954
}
961
955
962
- impl < T , const N : usize > Drop for Guard < ' _ , T , N > {
956
+ impl < T > Drop for Guard < ' _ , T > {
963
957
fn drop ( & mut self ) {
964
- debug_assert ! ( self . initialized <= N ) ;
958
+ debug_assert ! ( self . initialized <= self . array_mut . len ( ) ) ;
965
959
966
960
// SAFETY: this slice will contain only initialized objects.
967
961
unsafe {
@@ -972,15 +966,33 @@ impl<T, const N: usize> Drop for Guard<'_, T, N> {
972
966
}
973
967
}
974
968
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`].
977
981
#[ 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 > > {
984
985
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
+ }
986
998
}
0 commit comments