Skip to content

Commit 00cc349

Browse files
committed
core: introduce split_at{,_mut}_checked
Introduce split_at_checked and split_at_mut_checked methods to slices types (including str) which are non-panicking versions of split_at and split_at_mut respectively. This is analogous to get method being non-panicking version of indexing.
1 parent c9c9f52 commit 00cc349

File tree

3 files changed

+214
-30
lines changed

3 files changed

+214
-30
lines changed

library/core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@
185185
#![feature(set_ptr_value)]
186186
#![feature(slice_ptr_get)]
187187
#![feature(slice_split_at_unchecked)]
188+
#![feature(split_at_checked)]
188189
#![feature(str_internals)]
189190
#![feature(str_split_inclusive_remainder)]
190191
#![feature(str_split_remainder)]

library/core/src/slice/mod.rs

+104-11
Original file line numberDiff line numberDiff line change
@@ -1832,7 +1832,8 @@ impl<T> [T] {
18321832
///
18331833
/// # Panics
18341834
///
1835-
/// Panics if `mid > len`.
1835+
/// Panics if `mid > len`. For non-panicking alternative see
1836+
/// [`split_at_checked`](slice::split_at_checked).
18361837
///
18371838
/// # Examples
18381839
///
@@ -1859,15 +1860,15 @@ impl<T> [T] {
18591860
/// ```
18601861
#[stable(feature = "rust1", since = "1.0.0")]
18611862
#[rustc_const_stable(feature = "const_slice_split_at_not_mut", since = "1.71.0")]
1862-
#[rustc_allow_const_fn_unstable(slice_split_at_unchecked)]
1863+
#[rustc_allow_const_fn_unstable(split_at_checked)]
18631864
#[inline]
18641865
#[track_caller]
18651866
#[must_use]
18661867
pub const fn split_at(&self, mid: usize) -> (&[T], &[T]) {
1867-
assert!(mid <= self.len());
1868-
// SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which
1869-
// fulfills the requirements of `split_at_unchecked`.
1870-
unsafe { self.split_at_unchecked(mid) }
1868+
match self.split_at_checked(mid) {
1869+
Some(pair) => pair,
1870+
None => panic!("mid > len"),
1871+
}
18711872
}
18721873

18731874
/// Divides one mutable slice into two at an index.
@@ -1878,7 +1879,8 @@ impl<T> [T] {
18781879
///
18791880
/// # Panics
18801881
///
1881-
/// Panics if `mid > len`.
1882+
/// Panics if `mid > len`. For non-panicking alternative see
1883+
/// [`split_at_mut_checked`](slice::split_at_mut_checked).
18821884
///
18831885
/// # Examples
18841886
///
@@ -1897,10 +1899,10 @@ impl<T> [T] {
18971899
#[must_use]
18981900
#[rustc_const_unstable(feature = "const_slice_split_at_mut", issue = "101804")]
18991901
pub const fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) {
1900-
assert!(mid <= self.len());
1901-
// SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which
1902-
// fulfills the requirements of `from_raw_parts_mut`.
1903-
unsafe { self.split_at_mut_unchecked(mid) }
1902+
match self.split_at_mut_checked(mid) {
1903+
Some(pair) => pair,
1904+
None => panic!("mid > len"),
1905+
}
19041906
}
19051907

19061908
/// Divides one slice into two at an index, without doing bounds checking.
@@ -2019,6 +2021,97 @@ impl<T> [T] {
20192021
unsafe { (from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid)) }
20202022
}
20212023

2024+
/// Divides one slice into two at an index returning `None` if slice is too
2025+
/// short.
2026+
///
2027+
/// The first will contain all indices from `[0, mid)` (excluding
2028+
/// the index `mid` itself) and the second will contain all
2029+
/// indices from `[mid, len)` (excluding the index `len` itself).
2030+
///
2031+
/// Returns `None` if `mid > len`.
2032+
///
2033+
/// # Examples
2034+
///
2035+
/// ```
2036+
/// #![feature(split_at_checked)]
2037+
///
2038+
/// let v = [1, 2, 3, 4, 5, 6];
2039+
///
2040+
/// {
2041+
/// let (left, right) = v.split_at_checked(0).unwrap();
2042+
/// assert_eq!(left, []);
2043+
/// assert_eq!(right, [1, 2, 3, 4, 5, 6]);
2044+
/// }
2045+
///
2046+
/// {
2047+
/// let (left, right) = v.split_at_checked(2).unwrap();
2048+
/// assert_eq!(left, [1, 2]);
2049+
/// assert_eq!(right, [3, 4, 5, 6]);
2050+
/// }
2051+
///
2052+
/// {
2053+
/// let (left, right) = v.split_at_checked(6).unwrap();
2054+
/// assert_eq!(left, [1, 2, 3, 4, 5, 6]);
2055+
/// assert_eq!(right, []);
2056+
/// }
2057+
///
2058+
/// assert_eq!(None, v.split_at_checked(7));
2059+
/// ```
2060+
#[unstable(feature = "split_at_checked", reason = "new API", issue = "119128")]
2061+
#[rustc_const_unstable(feature = "split_at_checked", issue = "119128")]
2062+
#[inline]
2063+
#[track_caller]
2064+
#[must_use]
2065+
pub const fn split_at_checked(&self, mid: usize) -> Option<(&[T], &[T])> {
2066+
if mid <= self.len() {
2067+
// SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which
2068+
// fulfills the requirements of `split_at_unchecked`.
2069+
Some(unsafe { self.split_at_unchecked(mid) })
2070+
} else {
2071+
None
2072+
}
2073+
}
2074+
2075+
/// Divides one mutable slice into two at an index.
2076+
///
2077+
/// The first will contain all indices from `[0, mid)` (excluding
2078+
/// the index `mid` itself) and the second will contain all
2079+
/// indices from `[mid, len)` (excluding the index `len` itself).
2080+
///
2081+
/// Returns `None` if `mid > len`.
2082+
///
2083+
/// # Examples
2084+
///
2085+
/// ```
2086+
/// #![feature(split_at_checked)]
2087+
///
2088+
/// let mut v = [1, 0, 3, 0, 5, 6];
2089+
///
2090+
/// if let Some((left, right)) = v.split_at_mut_checked(2) {
2091+
/// assert_eq!(left, [1, 0]);
2092+
/// assert_eq!(right, [3, 0, 5, 6]);
2093+
/// left[1] = 2;
2094+
/// right[1] = 4;
2095+
/// }
2096+
/// assert_eq!(v, [1, 2, 3, 4, 5, 6]);
2097+
///
2098+
/// assert_eq!(None, v.split_at_mut_checked(7));
2099+
/// ```
2100+
#[unstable(feature = "split_at_checked", reason = "new API", issue = "119128")]
2101+
#[rustc_const_unstable(feature = "split_at_checked", issue = "119128")]
2102+
#[inline]
2103+
#[track_caller]
2104+
#[must_use]
2105+
pub const fn split_at_mut_checked(&mut self, mid: usize) -> Option<(&mut [T], &mut [T])> {
2106+
if mid <= self.len() {
2107+
// SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which
2108+
// fulfills the requirements of `split_at_unchecked`.
2109+
Some(unsafe { self.split_at_mut_unchecked(mid) })
2110+
} else {
2111+
None
2112+
}
2113+
}
2114+
20222115
/// Divides one slice into an array and a remainder slice at an index.
20232116
///
20242117
/// 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 [`split_at_checked`](str::split_at_checked).
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.split_at_checked(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 [`split_at_mut_checked`](str::split_at_mut_checked).
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 [`split_at_mut_checked`]
718+
/// method.
719+
///
720+
/// [`split_at_mut_checked`]: str::split_at_mut_checked
721+
///
722+
/// # Examples
723+
///
724+
/// ```
725+
/// #![feature(split_at_checked)]
726+
///
727+
/// let s = "Per Martin-Löf";
728+
///
729+
/// let (first, last) = s.split_at_checked(3).unwrap();
730+
/// assert_eq!("Per", first);
731+
/// assert_eq!(" Martin-Löf", last);
732+
///
733+
/// assert_eq!(None, s.split_at_checked(13)); // Inside “ö”
734+
/// assert_eq!(None, s.split_at_checked(16)); // Beyond the string length
735+
/// ```
736+
#[inline]
737+
#[must_use]
738+
#[unstable(feature = "split_at_checked", reason = "new API", issue = "119128")]
739+
pub fn split_at_checked(&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 [`split_at_checked`] method.
759+
///
760+
/// [`split_at_checked`]: str::split_at_checked
761+
///
762+
/// # Examples
763+
///
764+
/// ```
765+
/// #![feature(split_at_checked)]
766+
///
767+
/// let mut s = "Per Martin-Löf".to_string();
768+
/// if let Some((first, last)) = s.split_at_mut_checked(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.split_at_mut_checked(13)); // Inside “ö”
776+
/// assert_eq!(None, s.split_at_mut_checked(16)); // Beyond the string length
777+
/// ```
778+
#[inline]
779+
#[must_use]
780+
#[unstable(feature = "split_at_checked", reason = "new API", issue = "119128")]
781+
pub fn split_at_mut_checked(&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)