@@ -14,6 +14,7 @@ use crate::mem::{self, MaybeUninit};
1414use crate :: ops:: {
1515 ChangeOutputType , ControlFlow , FromResidual , Index , IndexMut , NeverShortCircuit , Residual , Try ,
1616} ;
17+ use crate :: ptr;
1718use crate :: slice:: { Iter , IterMut } ;
1819
1920mod equality;
@@ -229,6 +230,43 @@ impl<'a, T, const N: usize> TryFrom<&'a mut [T]> for &'a mut [T; N] {
229230 }
230231}
231232
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+
232270/// The hash of an array is the same as that of the corresponding slice,
233271/// as required by the `Borrow` implementation.
234272///
@@ -800,26 +838,7 @@ where
800838 return unsafe { Some ( Try :: from_output ( mem:: zeroed ( ) ) ) } ;
801839 }
802840
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 ( ) ;
823842
824843 while let Some ( item_rslt) = iter. next ( ) {
825844 let item = match item_rslt. branch ( ) {
@@ -829,22 +848,17 @@ where
829848 ControlFlow :: Continue ( elem) => elem,
830849 } ;
831850
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) } ;
839854
840855 // Check if the whole array was initialized.
841- if guard. initialized == N {
842- mem:: forget ( guard) ;
843-
856+ if guard. is_complete ( ) {
844857 // SAFETY: the condition above asserts that all elements are
845858 // 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+ }
848862 }
849863 }
850864
@@ -853,3 +867,65 @@ where
853867 // dropping all already initialized elements.
854868 None
855869}
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