Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Vec::splice and String::splice (RFC 1432) #40434

Merged
merged 6 commits into from
Apr 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/doc/unstable-book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@
- [slice_rsplit](library-features/slice-rsplit.md)
- [sort_internals](library-features/sort-internals.md)
- [sort_unstable](library-features/sort-unstable.md)
- [splice](library-features/splice.md)
- [step_by](library-features/step-by.md)
- [step_trait](library-features/step-trait.md)
- [str_checked_slicing](library-features/str-checked-slicing.md)
Expand Down
24 changes: 24 additions & 0 deletions src/doc/unstable-book/src/library-features/splice.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# `splice`

The tracking issue for this feature is: [#32310]

[#32310]: https://github.com/rust-lang/rust/issues/32310

------------------------

The `splice()` method on `Vec` and `String` allows you to replace a range
of values in a vector or string with another range of values, and returns
the replaced values.

A simple example:

```rust
#![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");
```
6 changes: 1 addition & 5 deletions src/libcollections/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1519,13 +1519,9 @@ impl<T: Clone> ToOwned for [T] {
self.to_vec()
}

// HACK(japaric): with cfg(test) the inherent `[T]::to_vec`, which is required for this method
// definition, is not available. Since we don't require this method for testing purposes, I'll
// just stub it
// NB see the slice::hack module in slice.rs for more information
#[cfg(test)]
fn to_owned(&self) -> Vec<T> {
panic!("not available with cfg(test)")
hack::to_vec(self)
}

fn clone_into(&self, target: &mut Vec<T>) {
Expand Down
125 changes: 124 additions & 1 deletion src/libcollections/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1316,7 +1316,7 @@ impl String {
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
Expand Down Expand Up @@ -1382,6 +1382,71 @@ impl String {
}
}

/// 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.
Expand Down Expand Up @@ -2145,3 +2210,61 @@ impl<'a> DoubleEndedIterator for Drain<'a> {

#[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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible for this to use Vec::splice?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean using the Vec implementation for String in general, or using Vec::splice() in the string splice drop impl?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general it may be pretty hard due to char decoding and whatnot, but here in drop it seems like it'd be a one-liner to call the vec impl for splicing I think, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look into it after I rebase

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()
}
}
1 change: 1 addition & 0 deletions src/libcollections/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#![feature(pattern)]
#![feature(placement_in_syntax)]
#![feature(rand)]
#![feature(splice)]
#![feature(step_by)]
#![feature(str_escape)]
#![feature(test)]
Expand Down
63 changes: 63 additions & 0 deletions src/libcollections/tests/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,69 @@ fn test_drain() {
assert_eq!(t, "");
}

#[test]
fn test_splice() {
let mut s = "Hello, world!".to_owned();
let t: String = s.splice(7..12, "世界").collect();
assert_eq!(s, "Hello, 世界!");
assert_eq!(t, "world");
}

#[test]
#[should_panic]
fn test_splice_char_boundary() {
let mut s = "Hello, 世界!".to_owned();
s.splice(..8, "");
}

#[test]
fn test_splice_inclusive_range() {
let mut v = String::from("12345");
let t: String = v.splice(2...3, "789").collect();
assert_eq!(v, "127895");
assert_eq!(t, "34");
let t2: String = v.splice(1...2, "A").collect();
assert_eq!(v, "1A895");
assert_eq!(t2, "27");
}

#[test]
#[should_panic]
fn test_splice_out_of_bounds() {
let mut s = String::from("12345");
s.splice(5..6, "789");
}

#[test]
#[should_panic]
fn test_splice_inclusive_out_of_bounds() {
let mut s = String::from("12345");
s.splice(5...5, "789");
}

#[test]
fn test_splice_empty() {
let mut s = String::from("12345");
let t: String = s.splice(1..2, "").collect();
assert_eq!(s, "1345");
assert_eq!(t, "2");
}

#[test]
fn test_splice_unbounded() {
let mut s = String::from("12345");
let t: String = s.splice(.., "").collect();
assert_eq!(s, "");
assert_eq!(t, "12345");
}

#[test]
fn test_splice_forget() {
let mut s = String::from("12345");
::std::mem::forget(s.splice(2..4, "789"));
assert_eq!(s, "12345");
}

#[test]
fn test_extend_ref() {
let mut a = "foo".to_string();
Expand Down
63 changes: 63 additions & 0 deletions src/libcollections/tests/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,69 @@ fn test_drain_inclusive_out_of_bounds() {
v.drain(5...5);
}

#[test]
fn test_splice() {
let mut v = vec![1, 2, 3, 4, 5];
let a = [10, 11, 12];
v.splice(2..4, a.iter().cloned());
assert_eq!(v, &[1, 2, 10, 11, 12, 5]);
v.splice(1..3, Some(20));
assert_eq!(v, &[1, 20, 11, 12, 5]);
}

#[test]
fn test_splice_inclusive_range() {
let mut v = vec![1, 2, 3, 4, 5];
let a = [10, 11, 12];
let t1: Vec<_> = v.splice(2...3, a.iter().cloned()).collect();
assert_eq!(v, &[1, 2, 10, 11, 12, 5]);
assert_eq!(t1, &[3, 4]);
let t2: Vec<_> = v.splice(1...2, Some(20)).collect();
assert_eq!(v, &[1, 20, 11, 12, 5]);
assert_eq!(t2, &[2, 10]);
}

#[test]
#[should_panic]
fn test_splice_out_of_bounds() {
let mut v = vec![1, 2, 3, 4, 5];
let a = [10, 11, 12];
v.splice(5..6, a.iter().cloned());
}

#[test]
#[should_panic]
fn test_splice_inclusive_out_of_bounds() {
let mut v = vec![1, 2, 3, 4, 5];
let a = [10, 11, 12];
v.splice(5...5, a.iter().cloned());
}

#[test]
fn test_splice_items_zero_sized() {
let mut vec = vec![(), (), ()];
let vec2 = vec![];
let t: Vec<_> = vec.splice(1..2, vec2.iter().cloned()).collect();
assert_eq!(vec, &[(), ()]);
assert_eq!(t, &[()]);
}

#[test]
fn test_splice_unbounded() {
let mut vec = vec![1, 2, 3, 4, 5];
let t: Vec<_> = vec.splice(.., None).collect();
assert_eq!(vec, &[]);
assert_eq!(t, &[1, 2, 3, 4, 5]);
}

#[test]
fn test_splice_forget() {
let mut v = vec![1, 2, 3, 4, 5];
let a = [10, 11, 12];
::std::mem::forget(v.splice(2..4, a.iter().cloned()));
assert_eq!(v, &[1, 2]);
}

#[test]
fn test_into_boxed_slice() {
let xs = vec![1, 2, 3];
Expand Down
Loading