Skip to content

Commit

Permalink
Auto merge of #118578 - mina86:c, r=dtolnay
Browse files Browse the repository at this point in the history
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.

- rust-lang/libs-team#308
- #119128
  • Loading branch information
bors committed Jan 22, 2024
2 parents a58ec8f + 50cbbef commit e99b66b
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 28 deletions.
1 change: 1 addition & 0 deletions library/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@
#![feature(set_ptr_value)]
#![feature(slice_ptr_get)]
#![feature(slice_split_at_unchecked)]
#![feature(split_at_checked)]
#![feature(str_internals)]
#![feature(str_split_inclusive_remainder)]
#![feature(str_split_remainder)]
Expand Down
115 changes: 105 additions & 10 deletions library/core/src/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1842,7 +1842,8 @@ impl<T> [T] {
///
/// # Panics
///
/// Panics if `mid > len`.
/// Panics if `mid > len`. For a non-panicking alternative see
/// [`split_at_checked`](slice::split_at_checked).
///
/// # Examples
///
Expand All @@ -1869,14 +1870,15 @@ impl<T> [T] {
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_slice_split_at_not_mut", since = "1.71.0")]
#[rustc_allow_const_fn_unstable(split_at_checked)]
#[inline]
#[track_caller]
#[must_use]
pub const fn split_at(&self, mid: usize) -> (&[T], &[T]) {
assert!(mid <= self.len());
// SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which
// fulfills the requirements of `split_at_unchecked`.
unsafe { self.split_at_unchecked(mid) }
match self.split_at_checked(mid) {
Some(pair) => pair,
None => panic!("mid > len"),
}
}

/// Divides one mutable slice into two at an index.
Expand All @@ -1887,7 +1889,8 @@ impl<T> [T] {
///
/// # Panics
///
/// Panics if `mid > len`.
/// Panics if `mid > len`. For a non-panicking alternative see
/// [`split_at_mut_checked`](slice::split_at_mut_checked).
///
/// # Examples
///
Expand All @@ -1906,10 +1909,10 @@ impl<T> [T] {
#[must_use]
#[rustc_const_unstable(feature = "const_slice_split_at_mut", issue = "101804")]
pub const fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) {
assert!(mid <= self.len());
// SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which
// fulfills the requirements of `from_raw_parts_mut`.
unsafe { self.split_at_mut_unchecked(mid) }
match self.split_at_mut_checked(mid) {
Some(pair) => pair,
None => panic!("mid > len"),
}
}

/// Divides one slice into two at an index, without doing bounds checking.
Expand Down Expand Up @@ -2031,6 +2034,98 @@ impl<T> [T] {
unsafe { (from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid)) }
}

/// Divides one slice into two at an index, returning `None` if the slice is
/// too short.
///
/// If `mid ≤ len` returns a pair of slices where the first will contain all
/// indices from `[0, mid)` (excluding the index `mid` itself) and the
/// second will contain all indices from `[mid, len)` (excluding the index
/// `len` itself).
///
/// Otherwise, if `mid > len`, returns `None`.
///
/// # Examples
///
/// ```
/// #![feature(split_at_checked)]
///
/// let v = [1, -2, 3, -4, 5, -6];
///
/// {
/// let (left, right) = v.split_at_checked(0).unwrap();
/// assert_eq!(left, []);
/// assert_eq!(right, [1, -2, 3, -4, 5, -6]);
/// }
///
/// {
/// let (left, right) = v.split_at_checked(2).unwrap();
/// assert_eq!(left, [1, -2]);
/// assert_eq!(right, [3, -4, 5, -6]);
/// }
///
/// {
/// let (left, right) = v.split_at_checked(6).unwrap();
/// assert_eq!(left, [1, -2, 3, -4, 5, -6]);
/// assert_eq!(right, []);
/// }
///
/// assert_eq!(None, v.split_at_checked(7));
/// ```
#[unstable(feature = "split_at_checked", reason = "new API", issue = "119128")]
#[rustc_const_unstable(feature = "split_at_checked", issue = "119128")]
#[inline]
#[must_use]
pub const fn split_at_checked(&self, mid: usize) -> Option<(&[T], &[T])> {
if mid <= self.len() {
// SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which
// fulfills the requirements of `split_at_unchecked`.
Some(unsafe { self.split_at_unchecked(mid) })
} else {
None
}
}

/// Divides one mutable slice into two at an index, returning `None` if the
/// slice is too short.
///
/// If `mid ≤ len` returns a pair of slices where the first will contain all
/// indices from `[0, mid)` (excluding the index `mid` itself) and the
/// second will contain all indices from `[mid, len)` (excluding the index
/// `len` itself).
///
/// Otherwise, if `mid > len`, returns `None`.
///
/// # Examples
///
/// ```
/// #![feature(split_at_checked)]
///
/// let mut v = [1, 0, 3, 0, 5, 6];
///
/// if let Some((left, right)) = v.split_at_mut_checked(2) {
/// assert_eq!(left, [1, 0]);
/// assert_eq!(right, [3, 0, 5, 6]);
/// left[1] = 2;
/// right[1] = 4;
/// }
/// assert_eq!(v, [1, 2, 3, 4, 5, 6]);
///
/// assert_eq!(None, v.split_at_mut_checked(7));
/// ```
#[unstable(feature = "split_at_checked", reason = "new API", issue = "119128")]
#[rustc_const_unstable(feature = "split_at_checked", issue = "119128")]
#[inline]
#[must_use]
pub const fn split_at_mut_checked(&mut self, mid: usize) -> Option<(&mut [T], &mut [T])> {
if mid <= self.len() {
// SAFETY: `[ptr; mid]` and `[mid; len]` are inside `self`, which
// fulfills the requirements of `split_at_unchecked`.
Some(unsafe { self.split_at_mut_unchecked(mid) })
} else {
None
}
}

/// Returns an iterator over subslices separated by elements that match
/// `pred`. The matched element is not contained in the subslices.
///
Expand Down
129 changes: 111 additions & 18 deletions library/core/src/str/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -641,8 +641,9 @@ impl str {
///
/// # Panics
///
/// Panics if `mid` is not on a UTF-8 code point boundary, or if it is
/// past the end of the last code point of the string slice.
/// Panics if `mid` is not on a UTF-8 code point boundary, or if it is past
/// the end of the last code point of the string slice. For a non-panicking
/// alternative see [`split_at_checked`](str::split_at_checked).
///
/// # Examples
///
Expand All @@ -658,12 +659,9 @@ impl str {
#[must_use]
#[stable(feature = "str_split_at", since = "1.4.0")]
pub fn split_at(&self, mid: usize) -> (&str, &str) {
// is_char_boundary checks that the index is in [0, .len()]
if self.is_char_boundary(mid) {
// SAFETY: just checked that `mid` is on a char boundary.
unsafe { (self.get_unchecked(0..mid), self.get_unchecked(mid..self.len())) }
} else {
slice_error_fail(self, 0, mid)
match self.split_at_checked(mid) {
None => slice_error_fail(self, 0, mid),
Some(pair) => pair,
}
}

Expand All @@ -681,8 +679,9 @@ impl str {
///
/// # Panics
///
/// Panics if `mid` is not on a UTF-8 code point boundary, or if it is
/// past the end of the last code point of the string slice.
/// Panics if `mid` is not on a UTF-8 code point boundary, or if it is past
/// the end of the last code point of the string slice. For a non-panicking
/// alternative see [`split_at_mut_checked`](str::split_at_mut_checked).
///
/// # Examples
///
Expand All @@ -702,20 +701,114 @@ impl str {
pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) {
// is_char_boundary checks that the index is in [0, .len()]
if self.is_char_boundary(mid) {
let len = self.len();
let ptr = self.as_mut_ptr();
// SAFETY: just checked that `mid` is on a char boundary.
unsafe {
(
from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr, mid)),
from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr.add(mid), len - mid)),
)
}
unsafe { self.split_at_mut_unchecked(mid) }
} else {
slice_error_fail(self, 0, mid)
}
}

/// Divide one string slice into two at an index.
///
/// The argument, `mid`, should be a valid byte offset from the start of the
/// string. It must also be on the boundary of a UTF-8 code point. The
/// method returns `None` if that’s not the case.
///
/// The two slices returned go from the start of the string slice to `mid`,
/// and from `mid` to the end of the string slice.
///
/// To get mutable string slices instead, see the [`split_at_mut_checked`]
/// method.
///
/// [`split_at_mut_checked`]: str::split_at_mut_checked
///
/// # Examples
///
/// ```
/// #![feature(split_at_checked)]
///
/// let s = "Per Martin-Löf";
///
/// let (first, last) = s.split_at_checked(3).unwrap();
/// assert_eq!("Per", first);
/// assert_eq!(" Martin-Löf", last);
///
/// assert_eq!(None, s.split_at_checked(13)); // Inside “ö”
/// assert_eq!(None, s.split_at_checked(16)); // Beyond the string length
/// ```
#[inline]
#[must_use]
#[unstable(feature = "split_at_checked", reason = "new API", issue = "119128")]
pub fn split_at_checked(&self, mid: usize) -> Option<(&str, &str)> {
// is_char_boundary checks that the index is in [0, .len()]
if self.is_char_boundary(mid) {
// SAFETY: just checked that `mid` is on a char boundary.
Some(unsafe { (self.get_unchecked(0..mid), self.get_unchecked(mid..self.len())) })
} else {
None
}
}

/// Divide one mutable string slice into two at an index.
///
/// The argument, `mid`, should be a valid byte offset from the start of the
/// string. It must also be on the boundary of a UTF-8 code point. The
/// method returns `None` if that’s not the case.
///
/// The two slices returned go from the start of the string slice to `mid`,
/// and from `mid` to the end of the string slice.
///
/// To get immutable string slices instead, see the [`split_at_checked`] method.
///
/// [`split_at_checked`]: str::split_at_checked
///
/// # Examples
///
/// ```
/// #![feature(split_at_checked)]
///
/// let mut s = "Per Martin-Löf".to_string();
/// if let Some((first, last)) = s.split_at_mut_checked(3) {
/// first.make_ascii_uppercase();
/// assert_eq!("PER", first);
/// assert_eq!(" Martin-Löf", last);
/// }
/// assert_eq!("PER Martin-Löf", s);
///
/// assert_eq!(None, s.split_at_mut_checked(13)); // Inside “ö”
/// assert_eq!(None, s.split_at_mut_checked(16)); // Beyond the string length
/// ```
#[inline]
#[must_use]
#[unstable(feature = "split_at_checked", reason = "new API", issue = "119128")]
pub fn split_at_mut_checked(&mut self, mid: usize) -> Option<(&mut str, &mut str)> {
// is_char_boundary checks that the index is in [0, .len()]
if self.is_char_boundary(mid) {
// SAFETY: just checked that `mid` is on a char boundary.
Some(unsafe { self.split_at_mut_unchecked(mid) })
} else {
None
}
}

/// Divide one string slice into two at an index.
///
/// # Safety
///
/// The caller must ensure that `mid` is a valid byte offset from the start
/// of the string and falls on the boundary of a UTF-8 code point.
unsafe fn split_at_mut_unchecked(&mut self, mid: usize) -> (&mut str, &mut str) {
let len = self.len();
let ptr = self.as_mut_ptr();
// SAFETY: caller guarantees `mid` is on a char boundary.
unsafe {
(
from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr, mid)),
from_utf8_unchecked_mut(slice::from_raw_parts_mut(ptr.add(mid), len - mid)),
)
}
}

/// Returns an iterator over the [`char`]s of a string slice.
///
/// As a string slice consists of valid UTF-8, we can iterate through a
Expand Down

0 comments on commit e99b66b

Please sign in to comment.