Skip to content

Commit b0ad48f

Browse files
committed
Add TryFrom<str> for [char; N].
1 parent 93e8201 commit b0ad48f

File tree

1 file changed

+108
-32
lines changed

1 file changed

+108
-32
lines changed

library/core/src/array/mod.rs

+108-32
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::mem::{self, MaybeUninit};
1414
use crate::ops::{
1515
ChangeOutputType, ControlFlow, FromResidual, Index, IndexMut, NeverShortCircuit, Residual, Try,
1616
};
17+
use crate::ptr;
1718
use crate::slice::{Iter, IterMut};
1819

1920
mod 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

Comments
 (0)