Auto merge of #40434 - mattico:splice-update, r=alexcrichton
Implement Vec::splice and String::splice (RFC 1432) RFC: rust-lang/rfcs#1432, tracking issue: #32310 A rebase of https://github.com/rust-lang/rust/pull/32355 with a few more tests. Let me know if you have any ideas for more tests. cc @SimonSapin
This commit is contained in:
commit
c7e724a148
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
```
|
|
@ -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>) {
|
||||
|
|
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#![feature(pattern)]
|
||||
#![feature(placement_in_syntax)]
|
||||
#![feature(rand)]
|
||||
#![feature(splice)]
|
||||
#![feature(step_by)]
|
||||
#![feature(str_escape)]
|
||||
#![feature(test)]
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -1057,13 +1057,13 @@ impl<T> Vec<T> {
|
|||
self.len += count;
|
||||
}
|
||||
|
||||
/// Create a draining iterator that removes the specified range in the vector
|
||||
/// Creates a draining iterator that removes the specified range in the vector
|
||||
/// and yields the removed items.
|
||||
///
|
||||
/// Note 1: The element range is removed even if the iterator is only
|
||||
/// partially consumed or not consumed at all.
|
||||
///
|
||||
/// Note 2: It is unspecified how many elements are removed from the vector,
|
||||
/// Note 2: It is unspecified how many elements are removed from the vector
|
||||
/// if the `Drain` value is leaked.
|
||||
///
|
||||
/// # Panics
|
||||
|
@ -1845,6 +1845,54 @@ impl<T> Vec<T> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a splicing iterator that replaces the specified range in the vector
|
||||
/// with the given `replace_with` iterator and yields the removed items.
|
||||
/// `replace_with` does not need to be the same length as `range`.
|
||||
///
|
||||
/// Note 1: The element range is removed even if the iterator is not
|
||||
/// consumed until the end.
|
||||
///
|
||||
/// Note 2: It is unspecified how many elements are removed from the vector,
|
||||
/// if the `Splice` value is leaked.
|
||||
///
|
||||
/// Note 3: The input iterator `replace_with` is only consumed
|
||||
/// when the `Splice` value is dropped.
|
||||
///
|
||||
/// Note 4: This is optimal if:
|
||||
///
|
||||
/// * The tail (elements in the vector after `range`) is empty,
|
||||
/// * or `replace_with` yields fewer elements than `range`’s length
|
||||
/// * or the lower bound of its `size_hint()` is exact.
|
||||
///
|
||||
/// Otherwise, a temporary vector is allocated and the tail is moved twice.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the starting point is greater than the end point or if
|
||||
/// the end point is greater than the length of the vector.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(splice)]
|
||||
/// let mut v = vec![1, 2, 3];
|
||||
/// let new = [7, 8];
|
||||
/// let u: Vec<_> = v.splice(..2, new.iter().cloned()).collect();
|
||||
/// assert_eq!(v, &[7, 8, 3]);
|
||||
/// assert_eq!(u, &[1, 2]);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
|
||||
pub fn splice<R, I>(&mut self, range: R, replace_with: I) -> Splice<I::IntoIter>
|
||||
where R: RangeArgument<usize>, I: IntoIterator<Item=T>
|
||||
{
|
||||
Splice {
|
||||
drain: self.drain(range),
|
||||
replace_with: replace_with.into_iter(),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[stable(feature = "extend_ref", since = "1.2.0")]
|
||||
|
@ -2344,3 +2392,125 @@ impl<'a, T> InPlace<T> for PlaceBack<'a, T> {
|
|||
&mut *ptr
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A splicing iterator for `Vec`.
|
||||
///
|
||||
/// This struct is created by the [`splice()`] method on [`Vec`]. See its
|
||||
/// documentation for more.
|
||||
///
|
||||
/// [`splice()`]: struct.Vec.html#method.splice
|
||||
/// [`Vec`]: struct.Vec.html
|
||||
#[derive(Debug)]
|
||||
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
|
||||
pub struct Splice<'a, I: Iterator + 'a> {
|
||||
drain: Drain<'a, I::Item>,
|
||||
replace_with: I,
|
||||
}
|
||||
|
||||
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
|
||||
impl<'a, I: Iterator> Iterator for Splice<'a, I> {
|
||||
type Item = I::Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.drain.next()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.drain.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
|
||||
impl<'a, I: Iterator> DoubleEndedIterator for Splice<'a, I> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.drain.next_back()
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
|
||||
impl<'a, I: Iterator> ExactSizeIterator for Splice<'a, I> {}
|
||||
|
||||
|
||||
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
|
||||
impl<'a, I: Iterator> Drop for Splice<'a, I> {
|
||||
fn drop(&mut self) {
|
||||
// exhaust drain first
|
||||
while let Some(_) = self.drain.next() {}
|
||||
|
||||
|
||||
unsafe {
|
||||
if self.drain.tail_len == 0 {
|
||||
let vec = &mut *self.drain.vec.as_mut_ptr();
|
||||
vec.extend(self.replace_with.by_ref());
|
||||
return
|
||||
}
|
||||
|
||||
// First fill the range left by drain().
|
||||
if !self.drain.fill(&mut self.replace_with) {
|
||||
return
|
||||
}
|
||||
|
||||
// There may be more elements. Use the lower bound as an estimate.
|
||||
// FIXME: Is the upper bound a better guess? Or something else?
|
||||
let (lower_bound, _upper_bound) = self.replace_with.size_hint();
|
||||
if lower_bound > 0 {
|
||||
self.drain.move_tail(lower_bound);
|
||||
if !self.drain.fill(&mut self.replace_with) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Collect any remaining elements.
|
||||
// This is a zero-length vector which does not allocate if `lower_bound` was exact.
|
||||
let mut collected = self.replace_with.by_ref().collect::<Vec<I::Item>>().into_iter();
|
||||
// Now we have an exact count.
|
||||
if collected.len() > 0 {
|
||||
self.drain.move_tail(collected.len());
|
||||
let filled = self.drain.fill(&mut collected);
|
||||
debug_assert!(filled);
|
||||
debug_assert_eq!(collected.len(), 0);
|
||||
}
|
||||
}
|
||||
// Let `Drain::drop` move the tail back if necessary and restore `vec.len`.
|
||||
}
|
||||
}
|
||||
|
||||
/// Private helper methods for `Splice::drop`
|
||||
impl<'a, T> Drain<'a, T> {
|
||||
/// The range from `self.vec.len` to `self.tail_start` contains elements
|
||||
/// that have been moved out.
|
||||
/// Fill that range as much as possible with new elements from the `replace_with` iterator.
|
||||
/// Return whether we filled the entire range. (`replace_with.next()` didn’t return `None`.)
|
||||
unsafe fn fill<I: Iterator<Item=T>>(&mut self, replace_with: &mut I) -> bool {
|
||||
let vec = &mut *self.vec.as_mut_ptr();
|
||||
let range_start = vec.len;
|
||||
let range_end = self.tail_start;
|
||||
let range_slice = slice::from_raw_parts_mut(
|
||||
vec.as_mut_ptr().offset(range_start as isize),
|
||||
range_end - range_start);
|
||||
|
||||
for place in range_slice {
|
||||
if let Some(new_item) = replace_with.next() {
|
||||
ptr::write(place, new_item);
|
||||
vec.len += 1;
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Make room for inserting more elements before the tail.
|
||||
unsafe fn move_tail(&mut self, extra_capacity: usize) {
|
||||
let vec = &mut *self.vec.as_mut_ptr();
|
||||
let used_capacity = self.tail_start + self.tail_len;
|
||||
vec.buf.reserve(used_capacity, extra_capacity);
|
||||
|
||||
let new_tail_start = self.tail_start + extra_capacity;
|
||||
let src = vec.as_ptr().offset(self.tail_start as isize);
|
||||
let dst = vec.as_mut_ptr().offset(new_tail_start as isize);
|
||||
ptr::copy(src, dst, self.tail_len);
|
||||
self.tail_start = new_tail_start;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue