Skip to content

Commit c7e724a

Browse files
committed
Auto merge of rust-lang#40434 - mattico:splice-update, r=alexcrichton
Implement Vec::splice and String::splice (RFC 1432) RFC: rust-lang/rfcs#1432, tracking issue: rust-lang#32310 A rebase of rust-lang#32355 with a few more tests. Let me know if you have any ideas for more tests. cc @SimonSapin
2 parents 0777c75 + feae5a0 commit c7e724a

File tree

8 files changed

+449
-8
lines changed

8 files changed

+449
-8
lines changed

src/doc/unstable-book/src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@
195195
- [slice_rsplit](library-features/slice-rsplit.md)
196196
- [sort_internals](library-features/sort-internals.md)
197197
- [sort_unstable](library-features/sort-unstable.md)
198+
- [splice](library-features/splice.md)
198199
- [step_by](library-features/step-by.md)
199200
- [step_trait](library-features/step-trait.md)
200201
- [str_checked_slicing](library-features/str-checked-slicing.md)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# `splice`
2+
3+
The tracking issue for this feature is: [#32310]
4+
5+
[#32310]: https://github.com/rust-lang/rust/issues/32310
6+
7+
------------------------
8+
9+
The `splice()` method on `Vec` and `String` allows you to replace a range
10+
of values in a vector or string with another range of values, and returns
11+
the replaced values.
12+
13+
A simple example:
14+
15+
```rust
16+
#![feature(splice)]
17+
let mut s = String::from("α is alpha, β is beta");
18+
let beta_offset = s.find('β').unwrap_or(s.len());
19+
20+
// Replace the range up until the β from the string
21+
let t: String = s.splice(..beta_offset, "Α is capital alpha; ").collect();
22+
assert_eq!(t, "α is alpha, ");
23+
assert_eq!(s, "Α is capital alpha; β is beta");
24+
```

src/libcollections/slice.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -1519,13 +1519,9 @@ impl<T: Clone> ToOwned for [T] {
15191519
self.to_vec()
15201520
}
15211521

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

15311527
fn clone_into(&self, target: &mut Vec<T>) {

src/libcollections/string.rs

+124-1
Original file line numberDiff line numberDiff line change
@@ -1316,7 +1316,7 @@ impl String {
13161316
self.vec.clear()
13171317
}
13181318

1319-
/// Create a draining iterator that removes the specified range in the string
1319+
/// Creates a draining iterator that removes the specified range in the string
13201320
/// and yields the removed chars.
13211321
///
13221322
/// Note: The element range is removed even if the iterator is not
@@ -1382,6 +1382,71 @@ impl String {
13821382
}
13831383
}
13841384

1385+
/// Creates a splicing iterator that removes the specified range in the string,
1386+
/// replaces with the given string, and yields the removed chars.
1387+
/// The given string doesn’t need to be the same length as the range.
1388+
///
1389+
/// Note: The element range is removed when the `Splice` is dropped,
1390+
/// even if the iterator is not consumed until the end.
1391+
///
1392+
/// # Panics
1393+
///
1394+
/// Panics if the starting point or end point do not lie on a [`char`]
1395+
/// boundary, or if they're out of bounds.
1396+
///
1397+
/// [`char`]: ../../std/primitive.char.html
1398+
///
1399+
/// # Examples
1400+
///
1401+
/// Basic usage:
1402+
///
1403+
/// ```
1404+
/// #![feature(splice)]
1405+
/// let mut s = String::from("α is alpha, β is beta");
1406+
/// let beta_offset = s.find('β').unwrap_or(s.len());
1407+
///
1408+
/// // Replace the range up until the β from the string
1409+
/// let t: String = s.splice(..beta_offset, "Α is capital alpha; ").collect();
1410+
/// assert_eq!(t, "α is alpha, ");
1411+
/// assert_eq!(s, "Α is capital alpha; β is beta");
1412+
/// ```
1413+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
1414+
pub fn splice<'a, 'b, R>(&'a mut self, range: R, replace_with: &'b str) -> Splice<'a, 'b>
1415+
where R: RangeArgument<usize>
1416+
{
1417+
// Memory safety
1418+
//
1419+
// The String version of Splice does not have the memory safety issues
1420+
// of the vector version. The data is just plain bytes.
1421+
// Because the range removal happens in Drop, if the Splice iterator is leaked,
1422+
// the removal will not happen.
1423+
let len = self.len();
1424+
let start = match range.start() {
1425+
Included(&n) => n,
1426+
Excluded(&n) => n + 1,
1427+
Unbounded => 0,
1428+
};
1429+
let end = match range.end() {
1430+
Included(&n) => n + 1,
1431+
Excluded(&n) => n,
1432+
Unbounded => len,
1433+
};
1434+
1435+
// Take out two simultaneous borrows. The &mut String won't be accessed
1436+
// until iteration is over, in Drop.
1437+
let self_ptr = self as *mut _;
1438+
// slicing does the appropriate bounds checks
1439+
let chars_iter = self[start..end].chars();
1440+
1441+
Splice {
1442+
start: start,
1443+
end: end,
1444+
iter: chars_iter,
1445+
string: self_ptr,
1446+
replace_with: replace_with
1447+
}
1448+
}
1449+
13851450
/// Converts this `String` into a `Box<str>`.
13861451
///
13871452
/// This will drop any excess capacity.
@@ -2145,3 +2210,61 @@ impl<'a> DoubleEndedIterator for Drain<'a> {
21452210

21462211
#[unstable(feature = "fused", issue = "35602")]
21472212
impl<'a> FusedIterator for Drain<'a> {}
2213+
2214+
/// A splicing iterator for `String`.
2215+
///
2216+
/// This struct is created by the [`splice()`] method on [`String`]. See its
2217+
/// documentation for more.
2218+
///
2219+
/// [`splice()`]: struct.String.html#method.splice
2220+
/// [`String`]: struct.String.html
2221+
#[derive(Debug)]
2222+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
2223+
pub struct Splice<'a, 'b> {
2224+
/// Will be used as &'a mut String in the destructor
2225+
string: *mut String,
2226+
/// Start of part to remove
2227+
start: usize,
2228+
/// End of part to remove
2229+
end: usize,
2230+
/// Current remaining range to remove
2231+
iter: Chars<'a>,
2232+
replace_with: &'b str,
2233+
}
2234+
2235+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
2236+
unsafe impl<'a, 'b> Sync for Splice<'a, 'b> {}
2237+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
2238+
unsafe impl<'a, 'b> Send for Splice<'a, 'b> {}
2239+
2240+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
2241+
impl<'a, 'b> Drop for Splice<'a, 'b> {
2242+
fn drop(&mut self) {
2243+
unsafe {
2244+
let vec = (*self.string).as_mut_vec();
2245+
vec.splice(self.start..self.end, self.replace_with.bytes());
2246+
}
2247+
}
2248+
}
2249+
2250+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
2251+
impl<'a, 'b> Iterator for Splice<'a, 'b> {
2252+
type Item = char;
2253+
2254+
#[inline]
2255+
fn next(&mut self) -> Option<char> {
2256+
self.iter.next()
2257+
}
2258+
2259+
fn size_hint(&self) -> (usize, Option<usize>) {
2260+
self.iter.size_hint()
2261+
}
2262+
}
2263+
2264+
#[unstable(feature = "splice", reason = "recently added", issue = "32310")]
2265+
impl<'a, 'b> DoubleEndedIterator for Splice<'a, 'b> {
2266+
#[inline]
2267+
fn next_back(&mut self) -> Option<char> {
2268+
self.iter.next_back()
2269+
}
2270+
}

src/libcollections/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#![feature(pattern)]
2121
#![feature(placement_in_syntax)]
2222
#![feature(rand)]
23+
#![feature(splice)]
2324
#![feature(step_by)]
2425
#![feature(str_escape)]
2526
#![feature(test)]

src/libcollections/tests/string.rs

+63
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,69 @@ fn test_drain() {
419419
assert_eq!(t, "");
420420
}
421421

422+
#[test]
423+
fn test_splice() {
424+
let mut s = "Hello, world!".to_owned();
425+
let t: String = s.splice(7..12, "世界").collect();
426+
assert_eq!(s, "Hello, 世界!");
427+
assert_eq!(t, "world");
428+
}
429+
430+
#[test]
431+
#[should_panic]
432+
fn test_splice_char_boundary() {
433+
let mut s = "Hello, 世界!".to_owned();
434+
s.splice(..8, "");
435+
}
436+
437+
#[test]
438+
fn test_splice_inclusive_range() {
439+
let mut v = String::from("12345");
440+
let t: String = v.splice(2...3, "789").collect();
441+
assert_eq!(v, "127895");
442+
assert_eq!(t, "34");
443+
let t2: String = v.splice(1...2, "A").collect();
444+
assert_eq!(v, "1A895");
445+
assert_eq!(t2, "27");
446+
}
447+
448+
#[test]
449+
#[should_panic]
450+
fn test_splice_out_of_bounds() {
451+
let mut s = String::from("12345");
452+
s.splice(5..6, "789");
453+
}
454+
455+
#[test]
456+
#[should_panic]
457+
fn test_splice_inclusive_out_of_bounds() {
458+
let mut s = String::from("12345");
459+
s.splice(5...5, "789");
460+
}
461+
462+
#[test]
463+
fn test_splice_empty() {
464+
let mut s = String::from("12345");
465+
let t: String = s.splice(1..2, "").collect();
466+
assert_eq!(s, "1345");
467+
assert_eq!(t, "2");
468+
}
469+
470+
#[test]
471+
fn test_splice_unbounded() {
472+
let mut s = String::from("12345");
473+
let t: String = s.splice(.., "").collect();
474+
assert_eq!(s, "");
475+
assert_eq!(t, "12345");
476+
}
477+
478+
#[test]
479+
fn test_splice_forget() {
480+
let mut s = String::from("12345");
481+
::std::mem::forget(s.splice(2..4, "789"));
482+
assert_eq!(s, "12345");
483+
}
484+
422485
#[test]
423486
fn test_extend_ref() {
424487
let mut a = "foo".to_string();

src/libcollections/tests/vec.rs

+63
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,69 @@ fn test_drain_inclusive_out_of_bounds() {
579579
v.drain(5...5);
580580
}
581581

582+
#[test]
583+
fn test_splice() {
584+
let mut v = vec![1, 2, 3, 4, 5];
585+
let a = [10, 11, 12];
586+
v.splice(2..4, a.iter().cloned());
587+
assert_eq!(v, &[1, 2, 10, 11, 12, 5]);
588+
v.splice(1..3, Some(20));
589+
assert_eq!(v, &[1, 20, 11, 12, 5]);
590+
}
591+
592+
#[test]
593+
fn test_splice_inclusive_range() {
594+
let mut v = vec![1, 2, 3, 4, 5];
595+
let a = [10, 11, 12];
596+
let t1: Vec<_> = v.splice(2...3, a.iter().cloned()).collect();
597+
assert_eq!(v, &[1, 2, 10, 11, 12, 5]);
598+
assert_eq!(t1, &[3, 4]);
599+
let t2: Vec<_> = v.splice(1...2, Some(20)).collect();
600+
assert_eq!(v, &[1, 20, 11, 12, 5]);
601+
assert_eq!(t2, &[2, 10]);
602+
}
603+
604+
#[test]
605+
#[should_panic]
606+
fn test_splice_out_of_bounds() {
607+
let mut v = vec![1, 2, 3, 4, 5];
608+
let a = [10, 11, 12];
609+
v.splice(5..6, a.iter().cloned());
610+
}
611+
612+
#[test]
613+
#[should_panic]
614+
fn test_splice_inclusive_out_of_bounds() {
615+
let mut v = vec![1, 2, 3, 4, 5];
616+
let a = [10, 11, 12];
617+
v.splice(5...5, a.iter().cloned());
618+
}
619+
620+
#[test]
621+
fn test_splice_items_zero_sized() {
622+
let mut vec = vec![(), (), ()];
623+
let vec2 = vec![];
624+
let t: Vec<_> = vec.splice(1..2, vec2.iter().cloned()).collect();
625+
assert_eq!(vec, &[(), ()]);
626+
assert_eq!(t, &[()]);
627+
}
628+
629+
#[test]
630+
fn test_splice_unbounded() {
631+
let mut vec = vec![1, 2, 3, 4, 5];
632+
let t: Vec<_> = vec.splice(.., None).collect();
633+
assert_eq!(vec, &[]);
634+
assert_eq!(t, &[1, 2, 3, 4, 5]);
635+
}
636+
637+
#[test]
638+
fn test_splice_forget() {
639+
let mut v = vec![1, 2, 3, 4, 5];
640+
let a = [10, 11, 12];
641+
::std::mem::forget(v.splice(2..4, a.iter().cloned()));
642+
assert_eq!(v, &[1, 2]);
643+
}
644+
582645
#[test]
583646
fn test_into_boxed_slice() {
584647
let xs = vec![1, 2, 3];

0 commit comments

Comments
 (0)