mut_str/
split.rs

1use core::{slice, str};
2
3#[must_use]
4/// Split a [`prim@str`] in units of UTF-8 characters.
5///
6/// ```
7/// use mut_str::char_split_at;
8///
9/// let s = "Hello, World!";
10///
11/// let (l, r) = char_split_at(s, 6).unwrap();
12/// assert_eq!(l, "Hello,");
13/// assert_eq!(r, " World!");
14/// ```
15pub fn char_split_at(s: &str, mid: usize) -> Option<(&str, &str)> {
16    let mut iter = s.char_indices();
17    let mut last_char = (0, '\x00');
18
19    for _ in 0..mid {
20        last_char = iter.next()?;
21    }
22
23    let mid_index = last_char.0 + last_char.1.len_utf8();
24
25    // SAFETY:
26    // `mid` is guaranteed to be on a char boundary and a maximum of one byte
27    // over the end of the slice.
28    Some(unsafe { (s.get_unchecked(..mid_index), s.get_unchecked(mid_index..)) })
29}
30
31#[must_use]
32/// Split a mutable [`prim@str`] in units of UTF-8 characters.
33///
34/// ```
35/// use mut_str::char_split_at_mut;
36///
37/// let mut owned_s = Box::<str>::from("Hello, World!");
38///
39/// let (l, r) = char_split_at_mut(&mut *owned_s, 6).unwrap();
40/// assert_eq!(l, "Hello,");
41/// assert_eq!(r, " World!");
42/// ```
43pub fn char_split_at_mut(s: &mut str, mid: usize) -> Option<(&mut str, &mut str)> {
44    let mut iter = s.char_indices();
45    let mut last_char = (0, '\x00');
46
47    for _ in 0..mid {
48        last_char = iter.next()?;
49    }
50
51    let mid_index = last_char.0 + last_char.1.len_utf8();
52
53    // SAFETY:
54    // `mid` is guaranteed to be on a char boundary and a maximum of one byte
55    // over the end of the slice.
56    Some(unsafe {
57        (
58            str::from_utf8_unchecked_mut(slice::from_raw_parts_mut(s.as_mut_ptr(), mid_index)),
59            s.get_unchecked_mut(mid_index..),
60        )
61    })
62}