]> git.lizzy.rs Git - rust.git/blobdiff - src/libcollections/string.rs
Auto merge of #41258 - clarcharr:str_box_extras, r=Kimundi
[rust.git] / src / libcollections / string.rs
index 8d6cf30511260b64ad83b54fecf1487b2f8dfd75..aa9628c535a997fb18939a2a7f66a85c61f10acb 100644 (file)
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
+use alloc::str as alloc_str;
+
 use core::fmt;
 use core::hash;
 use core::iter::{FromIterator, FusedIterator};
-use core::mem;
 use core::ops::{self, Add, AddAssign, Index, IndexMut};
 use core::ptr;
 use core::str as core_str;
@@ -1316,7 +1317,7 @@ pub fn clear(&mut self) {
         self.vec.clear()
     }
 
-    /// Create a draining iterator that removes the specified range in the string
+    /// Creates a draining iterator that removes the specified range in the string
     /// and yields the removed chars.
     ///
     /// Note: The element range is removed even if the iterator is not
@@ -1382,6 +1383,71 @@ pub fn drain<R>(&mut self, range: R) -> Drain
         }
     }
 
+    /// Creates a splicing iterator that removes the specified range in the string,
+    /// replaces with the given string, and yields the removed chars.
+    /// The given string doesn’t need to be the same length as the range.
+    ///
+    /// Note: The element range is removed when the `Splice` is dropped,
+    /// even if the iterator is not consumed until the end.
+    ///
+    /// # Panics
+    ///
+    /// Panics if the starting point or end point do not lie on a [`char`]
+    /// boundary, or if they're out of bounds.
+    ///
+    /// [`char`]: ../../std/primitive.char.html
+    ///
+    /// # Examples
+    ///
+    /// Basic usage:
+    ///
+    /// ```
+    /// #![feature(splice)]
+    /// let mut s = String::from("α is alpha, β is beta");
+    /// let beta_offset = s.find('β').unwrap_or(s.len());
+    ///
+    /// // Replace the range up until the β from the string
+    /// let t: String = s.splice(..beta_offset, "Α is capital alpha; ").collect();
+    /// assert_eq!(t, "α is alpha, ");
+    /// assert_eq!(s, "Α is capital alpha; β is beta");
+    /// ```
+    #[unstable(feature = "splice", reason = "recently added", issue = "32310")]
+    pub fn splice<'a, 'b, R>(&'a mut self, range: R, replace_with: &'b str) -> Splice<'a, 'b>
+        where R: RangeArgument<usize>
+    {
+        // Memory safety
+        //
+        // The String version of Splice does not have the memory safety issues
+        // of the vector version. The data is just plain bytes.
+        // Because the range removal happens in Drop, if the Splice iterator is leaked,
+        // the removal will not happen.
+        let len = self.len();
+        let start = match range.start() {
+             Included(&n) => n,
+             Excluded(&n) => n + 1,
+             Unbounded => 0,
+        };
+        let end = match range.end() {
+             Included(&n) => n + 1,
+             Excluded(&n) => n,
+             Unbounded => len,
+        };
+
+        // Take out two simultaneous borrows. The &mut String won't be accessed
+        // until iteration is over, in Drop.
+        let self_ptr = self as *mut _;
+        // slicing does the appropriate bounds checks
+        let chars_iter = self[start..end].chars();
+
+        Splice {
+            start: start,
+            end: end,
+            iter: chars_iter,
+            string: self_ptr,
+            replace_with: replace_with
+        }
+    }
+
     /// Converts this `String` into a `Box<str>`.
     ///
     /// This will drop any excess capacity.
@@ -1398,7 +1464,7 @@ pub fn drain<R>(&mut self, range: R) -> Drain
     #[stable(feature = "box_str", since = "1.4.0")]
     pub fn into_boxed_str(self) -> Box<str> {
         let slice = self.vec.into_boxed_slice();
-        unsafe { mem::transmute::<Box<[u8]>, Box<str>>(slice) }
+        unsafe { alloc_str::from_boxed_utf8_unchecked(slice) }
     }
 }
 
@@ -2145,3 +2211,61 @@ fn next_back(&mut self) -> Option<char> {
 
 #[unstable(feature = "fused", issue = "35602")]
 impl<'a> FusedIterator for Drain<'a> {}
+
+/// A splicing iterator for `String`.
+///
+/// This struct is created by the [`splice()`] method on [`String`]. See its
+/// documentation for more.
+///
+/// [`splice()`]: struct.String.html#method.splice
+/// [`String`]: struct.String.html
+#[derive(Debug)]
+#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
+pub struct Splice<'a, 'b> {
+    /// Will be used as &'a mut String in the destructor
+    string: *mut String,
+    /// Start of part to remove
+    start: usize,
+    /// End of part to remove
+    end: usize,
+    /// Current remaining range to remove
+    iter: Chars<'a>,
+    replace_with: &'b str,
+}
+
+#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
+unsafe impl<'a, 'b> Sync for Splice<'a, 'b> {}
+#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
+unsafe impl<'a, 'b> Send for Splice<'a, 'b> {}
+
+#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
+impl<'a, 'b> Drop for Splice<'a, 'b> {
+    fn drop(&mut self) {
+        unsafe {
+            let vec = (*self.string).as_mut_vec();
+            vec.splice(self.start..self.end, self.replace_with.bytes());
+        }
+    }
+}
+
+#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
+impl<'a, 'b> Iterator for Splice<'a, 'b> {
+    type Item = char;
+
+    #[inline]
+    fn next(&mut self) -> Option<char> {
+        self.iter.next()
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.iter.size_hint()
+    }
+}
+
+#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
+impl<'a, 'b> DoubleEndedIterator for Splice<'a, 'b> {
+    #[inline]
+    fn next_back(&mut self) -> Option<char> {
+        self.iter.next_back()
+    }
+}