@@ -14,6 +14,7 @@ use crate::mem::{self, MaybeUninit};
14
14
use crate :: ops:: {
15
15
ChangeOutputType , ControlFlow , FromResidual , Index , IndexMut , NeverShortCircuit , Residual , Try ,
16
16
} ;
17
+ use crate :: ptr;
17
18
use crate :: slice:: { Iter , IterMut } ;
18
19
19
20
mod equality;
@@ -229,6 +230,43 @@ impl<'a, T, const N: usize> TryFrom<&'a mut [T]> for &'a mut [T; N] {
229
230
}
230
231
}
231
232
233
+ #[ stable( feature = "char_array_from_str" , since = "1.60.0" ) ]
234
+ impl < const N : usize > TryFrom < & str > for [ char ; N ] {
235
+ type Error = TryFromSliceError ;
236
+
237
+ fn try_from ( text : & str ) -> Result < [ char ; N ] , TryFromSliceError > {
238
+ if N == 0 {
239
+ return if text. is_empty ( ) {
240
+ // SAFETY: An empty array is always inhabited and has no validity invariants.
241
+ Ok ( unsafe { mem:: zeroed ( ) } )
242
+ } else {
243
+ Err ( TryFromSliceError ( ( ) ) )
244
+ } ;
245
+ }
246
+
247
+ let mut guard = ArrayInitGuard :: new ( ) ;
248
+ let mut iter = text. chars ( ) ;
249
+
250
+ for _ in 0 ..N {
251
+ let next_ch = iter. next ( ) . ok_or ( TryFromSliceError ( ( ) ) ) ?;
252
+ // SAFETY: This is called at most N times (from loop range)
253
+ unsafe {
254
+ guard. add ( next_ch) ;
255
+ }
256
+ }
257
+
258
+ // SAFETY: There must be exactly `N` elements in the array
259
+ let out = unsafe { guard. into_inner ( ) . unwrap_unchecked ( ) } ;
260
+
261
+ if iter. next ( ) . is_some ( ) {
262
+ // too many chars in str
263
+ Err ( TryFromSliceError ( ( ) ) )
264
+ } else {
265
+ Ok ( out)
266
+ }
267
+ }
268
+ }
269
+
232
270
/// The hash of an array is the same as that of the corresponding slice,
233
271
/// as required by the `Borrow` implementation.
234
272
///
@@ -800,26 +838,7 @@ where
800
838
return unsafe { Some ( Try :: from_output ( mem:: zeroed ( ) ) ) } ;
801
839
}
802
840
803
- struct Guard < ' a , T , const N : usize > {
804
- array_mut : & ' a mut [ MaybeUninit < T > ; N ] ,
805
- initialized : usize ,
806
- }
807
-
808
- impl < T , const N : usize > Drop for Guard < ' _ , T , N > {
809
- fn drop ( & mut self ) {
810
- debug_assert ! ( self . initialized <= N ) ;
811
-
812
- // SAFETY: this slice will contain only initialized objects.
813
- unsafe {
814
- crate :: ptr:: drop_in_place ( MaybeUninit :: slice_assume_init_mut (
815
- & mut self . array_mut . get_unchecked_mut ( ..self . initialized ) ,
816
- ) ) ;
817
- }
818
- }
819
- }
820
-
821
- let mut array = MaybeUninit :: uninit_array :: < N > ( ) ;
822
- let mut guard = Guard { array_mut : & mut array, initialized : 0 } ;
841
+ let mut guard = ArrayInitGuard :: new ( ) ;
823
842
824
843
while let Some ( item_rslt) = iter. next ( ) {
825
844
let item = match item_rslt. branch ( ) {
@@ -829,22 +848,17 @@ where
829
848
ControlFlow :: Continue ( elem) => elem,
830
849
} ;
831
850
832
- // SAFETY: `guard.initialized` starts at 0, is increased by one in the
833
- // loop and the loop is aborted once it reaches N (which is
834
- // `array.len()`).
835
- unsafe {
836
- guard. array_mut . get_unchecked_mut ( guard. initialized ) . write ( item) ;
837
- }
838
- guard. initialized += 1 ;
851
+ // SAFETY: For N = 0, we do not enter this loop. For N > 0, we know guard is not full
852
+ // because the check below would return.
853
+ unsafe { guard. add ( item) } ;
839
854
840
855
// Check if the whole array was initialized.
841
- if guard. initialized == N {
842
- mem:: forget ( guard) ;
843
-
856
+ if guard. is_complete ( ) {
844
857
// SAFETY: the condition above asserts that all elements are
845
858
// initialized.
846
- let out = unsafe { MaybeUninit :: array_assume_init ( array) } ;
847
- return Some ( Try :: from_output ( out) ) ;
859
+ unsafe {
860
+ return Some ( Try :: from_output ( guard. into_inner ( ) . unwrap_unchecked ( ) ) ) ;
861
+ }
848
862
}
849
863
}
850
864
@@ -853,3 +867,65 @@ where
853
867
// dropping all already initialized elements.
854
868
None
855
869
}
870
+
871
+ /// Responsible for initializing an array and clearing up in case of a panic. Zero-cost and unsafe.
872
+ struct ArrayInitGuard < T , const N : usize > {
873
+ array : [ MaybeUninit < T > ; N ] ,
874
+ initialized : usize ,
875
+ }
876
+
877
+ impl < T , const N : usize > ArrayInitGuard < T , N > {
878
+ fn new ( ) -> Self {
879
+ Self { array : MaybeUninit :: uninit_array :: < N > ( ) , initialized : 0 }
880
+ }
881
+
882
+ /// Adds an element to the array
883
+ ///
884
+ /// # Safety
885
+ ///
886
+ /// This function must be called at most `N` times.
887
+ unsafe fn add ( & mut self , item : T ) {
888
+ debug_assert ! ( self . initialized < N ) ;
889
+ // SAFETY: `self.initialized` starts at 0, is increased by one each time `add` is called
890
+ // and `add` is called at most `N` times (from function contract)
891
+ unsafe {
892
+ self . array . get_unchecked_mut ( self . initialized ) . write ( item) ;
893
+ }
894
+ self . initialized += 1 ;
895
+ }
896
+
897
+ fn is_complete ( & self ) -> bool {
898
+ self . initialized == N
899
+ }
900
+
901
+ /// Safely take the contents of the array.
902
+ fn take_array ( & mut self ) -> [ MaybeUninit < T > ; N ] {
903
+ let array = mem:: replace ( & mut self . array , MaybeUninit :: uninit_array ( ) ) ;
904
+ // IMPORTANT: otherwise `drop` will try to drop elements that we have moved.
905
+ self . initialized = 0 ;
906
+ array
907
+ }
908
+
909
+ fn into_inner ( mut self ) -> Option < [ T ; N ] > {
910
+ if self . initialized == N {
911
+ let array = self . take_array ( ) ;
912
+ // SAFETY: All elements must have been initialized.
913
+ unsafe { Some ( MaybeUninit :: array_assume_init ( array) ) }
914
+ } else {
915
+ None
916
+ }
917
+ }
918
+ }
919
+
920
+ impl < T , const N : usize > Drop for ArrayInitGuard < T , N > {
921
+ fn drop ( & mut self ) {
922
+ debug_assert ! ( self . initialized <= N ) ;
923
+
924
+ // SAFETY: this slice will contain only initialized objects.
925
+ unsafe {
926
+ ptr:: drop_in_place ( MaybeUninit :: slice_assume_init_mut (
927
+ & mut self . array . get_unchecked_mut ( ..self . initialized ) ,
928
+ ) ) ;
929
+ }
930
+ }
931
+ }
0 commit comments