Skip to content

Commit e30b9c1

Browse files
committed
core: introduce checked_split_at{,_mut}
Introduce checked_split_at and checked_split_at_mut methods to slices types (including str) which are non-panicking versions of split_at and split_at_mut. This addition is analogous to how get method is non-panicking version of indexing.
1 parent 7ceaf19 commit e30b9c1

File tree

3 files changed

+215
-30
lines changed

3 files changed

+215
-30
lines changed

library/core/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -183,9 +183,11 @@
183183
#![feature(ptr_alignment_type)]
184184
#![feature(ptr_metadata)]
185185
#![feature(set_ptr_value)]
186+
#![feature(slice_checked_split_at)]
186187
#![feature(slice_ptr_get)]
187188
#![feature(slice_split_at_unchecked)]
188189
#![feature(str_internals)]
190+
#![feature(str_checked_split_at)]
189191
#![feature(str_split_inclusive_remainder)]
190192
#![feature(str_split_remainder)]
191193
#![feature(strict_provenance)]

library/core/src/slice/mod.rs

+104-11
Original file line numberDiff line numberDiff line change
@@ -1837,7 +1837,8 @@ impl<T> [T] {
18371837
///
18381838
/// # Panics
18391839
///
1840-
/// Panics if `mid > len`.
1840+
/// Panics if `mid > len`. For non-panicking alternative see
1841+
/// [`checked_split_at`](slice::checked_split_at).
18411842
///
18421843
/// # Examples
18431844
///
@@ -1864,15 +1865,15 @@ impl<T> [T] {
18641865
/// ```
18651866
#[stable(feature = "rust1", since = "1.0.0")]
18661867
#[rustc_const_stable(feature = "const_slice_split_at_not_mut", since = "1.71.0")]
1867-
#[rustc_allow_const_fn_unstable(slice_split_at_unchecked)]
1868+
#[rustc_allow_const_fn_unstable(slice_checked_split_at)]
18681869
#[inline]
18691870
#[track_caller]
18701871
#[must_use]
18711872
pub const fn split_at(&self, mid: usize) -> (&[T], &[T]) {
1872-
assert!(mid <= self.len());
1873-
// SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which
1874-
// fulfills the requirements of `split_at_unchecked`.
1875-
unsafe { self.split_at_unchecked(mid) }
1873+
match self.checked_split_at(mid) {
1874+
Some(pair) => pair,
1875+
None => panic!("mid > len"),
1876+
}
18761877
}
18771878

18781879
/// Divides one mutable slice into two at an index.
@@ -1883,7 +1884,8 @@ impl<T> [T] {
18831884
///
18841885
/// # Panics
18851886
///
1886-
/// Panics if `mid > len`.
1887+
/// Panics if `mid > len`. For non-panicking alternative see
1888+
/// [`checked_split_at_mut`](slice::checked_split_at_mut).
18871889
///
18881890
/// # Examples
18891891
///
@@ -1902,10 +1904,10 @@ impl<T> [T] {
19021904
#[must_use]
19031905
#[rustc_const_unstable(feature = "const_slice_split_at_mut", issue = "101804")]
19041906
pub const fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) {
1905-
assert!(mid <= self.len());
1906-
// SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which
1907-
// fulfills the requirements of `from_raw_parts_mut`.
1908-
unsafe { self.split_at_mut_unchecked(mid) }
1907+
match self.checked_split_at_mut(mid) {
1908+
Some(pair) => pair,
1909+
None => panic!("mid > len"),
1910+
}
19091911
}
19101912

19111913
/// Divides one slice into two at an index, without doing bounds checking.
@@ -2024,6 +2026,97 @@ impl<T> [T] {
20242026
unsafe { (from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid)) }
20252027
}
20262028

2029+
/// Divides one slice into two at an index returning `None` if slice is too
2030+
/// short.
2031+
///
2032+
/// The first will contain all indices from `[0, mid)` (excluding
2033+
/// the index `mid` itself) and the second will contain all
2034+
/// indices from `[mid, len)` (excluding the index `len` itself).
2035+
///
2036+
/// Returns `None` if `mid > len`.
2037+
///
2038+
/// # Examples
2039+
///
2040+
/// ```
2041+
/// #![feature(slice_checked_split_at)]
2042+
///
2043+
/// let v = [1, 2, 3, 4, 5, 6];
2044+
///
2045+
/// {
2046+
/// let (left, right) = v.checked_split_at(0).unwrap();
2047+
/// assert_eq!(left, []);
2048+
/// assert_eq!(right, [1, 2, 3, 4, 5, 6]);
2049+
/// }
2050+
///
2051+
/// {
2052+
/// let (left, right) = v.checked_split_at(2).unwrap();
2053+
/// assert_eq!(left, [1, 2]);
2054+
/// assert_eq!(right, [3, 4, 5, 6]);
2055+
/// }
2056+
///
2057+
/// {
2058+
/// let (left, right) = v.checked_split_at(6).unwrap();
2059+
/// assert_eq!(left, [1, 2, 3, 4, 5, 6]);
2060+
/// assert_eq!(right, []);
2061+
/// }
2062+
///
2063+
/// assert_eq!(None, v.checked_split_at(7));
2064+
/// ```
2065+
#[unstable(feature = "slice_checked_split_at", reason = "new API", issue = "118578")]
2066+
#[rustc_const_unstable(feature = "slice_checked_split_at", issue = "118578")]
2067+
#[inline]
2068+
#[track_caller]
2069+
#[must_use]
2070+
pub const fn checked_split_at(&self, mid: usize) -> Option<(&[T], &[T])> {
2071+
if mid <= self.len() {
2072+
// SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which
2073+
// fulfills the requirements of `split_at_unchecked`.
2074+
Some(unsafe { self.split_at_unchecked(mid) })
2075+
} else {
2076+
None
2077+
}
2078+
}
2079+
2080+
/// Divides one mutable slice into two at an index.
2081+
///
2082+
/// The first will contain all indices from `[0, mid)` (excluding
2083+
/// the index `mid` itself) and the second will contain all
2084+
/// indices from `[mid, len)` (excluding the index `len` itself).
2085+
///
2086+
/// Returns `None` if `mid > len`.
2087+
///
2088+
/// # Examples
2089+
///
2090+
/// ```
2091+
/// #![feature(slice_checked_split_at)]
2092+
///
2093+
/// let mut v = [1, 0, 3, 0, 5, 6];
2094+
///
2095+
/// if let Some((left, right)) = v.checked_split_at_mut(2) {
2096+
/// assert_eq!(left, [1, 0]);
2097+
/// assert_eq!(right, [3, 0, 5, 6]);
2098+
/// left[1] = 2;
2099+
/// right[1] = 4;
2100+
/// }
2101+
/// assert_eq!(v, [1, 2, 3, 4, 5, 6]);
2102+
///
2103+
/// assert_eq!(None, v.checked_split_at_mut(7));
2104+
/// ```
2105+
#[unstable(feature = "slice_checked_split_at", reason = "new API", issue = "118578")]
2106+
#[rustc_const_unstable(feature = "slice_checked_split_at", issue = "118578")]
2107+
#[inline]
2108+
#[track_caller]
2109+
#[must_use]
2110+
pub const fn checked_split_at_mut(&mut self, mid: usize) -> Option<(&mut [T], &mut [T])> {
2111+
if mid <= self.len() {
2112+
// SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which
2113+
// fulfills the requirements of `split_at_unchecked`.
2114+
Some(unsafe { self.split_at_mut_unchecked(mid) })
2115+
} else {
2116+
None
2117+
}
2118+
}
2119+
20272120
/// Divides one slice into an array and a remainder slice at an index.
20282121
///
20292122
/// The array will contain all indices from `[0, N)` (excluding

library/core/src/str/mod.rs

+109-19
Original file line numberDiff line numberDiff line change
@@ -641,8 +641,9 @@ impl str {
641641
///
642642
/// # Panics
643643
///
644-
/// Panics if `mid` is not on a UTF-8 code point boundary, or if it is
645-
/// past the end of the last code point of the string slice.
644+
/// Panics if `mid` is not on a UTF-8 code point boundary, or if it is past
645+
/// the end of the last code point of the string slice. For non-panicking
646+
/// alternative see [`checked_split_at`](str::checked_split_at).
646647
///
647648
/// # Examples
648649
///
@@ -658,13 +659,7 @@ impl str {
658659
#[must_use]
659660
#[stable(feature = "str_split_at", since = "1.4.0")]
660661
pub fn split_at(&self, mid: usize) -> (&str, &str) {
661-
// is_char_boundary checks that the index is in [0, .len()]
662-
if self.is_char_boundary(mid) {
663-
// SAFETY: just checked that `mid` is on a char boundary.
664-
unsafe { (self.get_unchecked(0..mid), self.get_unchecked(mid..self.len())) }
665-
} else {
666-
slice_error_fail(self, 0, mid)
667-
}
662+
self.checked_split_at(mid).unwrap_or_else(|| slice_error_fail(self, 0, mid))
668663
}
669664

670665
/// Divide one mutable string slice into two at an index.
@@ -681,8 +676,9 @@ impl str {
681676
///
682677
/// # Panics
683678
///
684-
/// Panics if `mid` is not on a UTF-8 code point boundary, or if it is
685-
/// past the end of the last code point of the string slice.
679+
/// Panics if `mid` is not on a UTF-8 code point boundary, or if it is past
680+
/// the end of the last code point of the string slice. For non-panicking
681+
/// alternative see [`checked_split_at_mut`](str::checked_split_at_mut).
686682
///
687683
/// # Examples
688684
///
@@ -702,20 +698,114 @@ impl str {
702698
pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) {
703699
// is_char_boundary checks that the index is in [0, .len()]
704700
if self.is_char_boundary(mid) {
705-
let len = self.len();
706-
let ptr = self.as_mut_ptr();
707701
// SAFETY: just checked that `mid` is on a char boundary.
708-
unsafe {
709-
(
710-
from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr, mid)),
711-
from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr.add(mid), len - mid)),
712-
)
713-
}
702+
unsafe { self.split_at_mut_unchecked(mid) }
714703
} else {
715704
slice_error_fail(self, 0, mid)
716705
}
717706
}
718707

708+
/// Divide one string slice into two at an index.
709+
///
710+
/// The argument, `mid`, should be a valid byte offset from the start of the
711+
/// string. It must also be on the boundary of a UTF-8 code point. The
712+
/// method returns `None` if that’s not the case.
713+
///
714+
/// The two slices returned go from the start of the string slice to `mid`,
715+
/// and from `mid` to the end of the string slice.
716+
///
717+
/// To get mutable string slices instead, see the [`checked_split_at_mut`]
718+
/// method.
719+
///
720+
/// [`split_at_mut`]: str::checked_split_at_mut
721+
///
722+
/// # Examples
723+
///
724+
/// ```
725+
/// #![feature(str_checked_split_at)]
726+
///
727+
/// let s = "Per Martin-Löf";
728+
///
729+
/// let (first, last) = s.checked_split_at(3).unwrap();
730+
/// assert_eq!("Per", first);
731+
/// assert_eq!(" Martin-Löf", last);
732+
///
733+
/// assert_eq!(None, s.checked_split_at(13)); // Inside “ö”
734+
/// assert_eq!(None, s.checked_split_at(16)); // Beyond the string length
735+
/// ```
736+
#[inline]
737+
#[must_use]
738+
#[unstable(feature = "str_checked_split_at", reason = "new API", issue = "118578")]
739+
pub fn checked_split_at(&self, mid: usize) -> Option<(&str, &str)> {
740+
// is_char_boundary checks that the index is in [0, .len()]
741+
if self.is_char_boundary(mid) {
742+
// SAFETY: just checked that `mid` is on a char boundary.
743+
Some(unsafe { (self.get_unchecked(0..mid), self.get_unchecked(mid..self.len())) })
744+
} else {
745+
None
746+
}
747+
}
748+
749+
/// Divide one mutable string slice into two at an index.
750+
///
751+
/// The argument, `mid`, should be a valid byte offset from the start of the
752+
/// string. It must also be on the boundary of a UTF-8 code point. The
753+
/// method returns `None` if that’s not the case.
754+
///
755+
/// The two slices returned go from the start of the string slice to `mid`,
756+
/// and from `mid` to the end of the string slice.
757+
///
758+
/// To get immutable string slices instead, see the [`checked_split_at`] method.
759+
///
760+
/// [`checked_split_at`]: str::checked_split_at
761+
///
762+
/// # Examples
763+
///
764+
/// ```
765+
/// #![feature(str_checked_split_at)]
766+
///
767+
/// let mut s = "Per Martin-Löf".to_string();
768+
/// if let Some((first, last)) = s.checked_split_at_mut(3) {
769+
/// first.make_ascii_uppercase();
770+
/// assert_eq!("PER", first);
771+
/// assert_eq!(" Martin-Löf", last);
772+
/// }
773+
/// assert_eq!("PER Martin-Löf", s);
774+
///
775+
/// assert_eq!(None, s.checked_split_at_mut(13)); // Inside “ö”
776+
/// assert_eq!(None, s.checked_split_at_mut(16)); // Beyond the string length
777+
/// ```
778+
#[inline]
779+
#[must_use]
780+
#[unstable(feature = "str_checked_split_at", reason = "new API", issue = "118578")]
781+
pub fn checked_split_at_mut(&mut self, mid: usize) -> Option<(&mut str, &mut str)> {
782+
// is_char_boundary checks that the index is in [0, .len()]
783+
if self.is_char_boundary(mid) {
784+
// SAFETY: just checked that `mid` is on a char boundary.
785+
Some(unsafe { self.split_at_mut_unchecked(mid) })
786+
} else {
787+
None
788+
}
789+
}
790+
791+
/// Divide one string slice into two at an index.
792+
///
793+
/// # Safety
794+
///
795+
/// The caller must ensure `mid` is a byte offset at a UTF-8 character
796+
/// boundary.
797+
unsafe fn split_at_mut_unchecked(&mut self, mid: usize) -> (&mut str, &mut str) {
798+
let len = self.len();
799+
let ptr = self.as_mut_ptr();
800+
// SAFETY: caller guarantees `mid` is on a char boundary.
801+
unsafe {
802+
(
803+
from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr, mid)),
804+
from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr.add(mid), len - mid)),
805+
)
806+
}
807+
}
808+
719809
/// Returns an iterator over the [`char`]s of a string slice.
720810
///
721811
/// As a string slice consists of valid UTF-8, we can iterate through a

0 commit comments

Comments
 (0)