@@ -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