Skip to content

Commit 8871c17

Browse files
committed
Auto merge of #24781 - bluss:vec-drain-range, r=alexcrichton
Implement Vec::drain(\<range type\>) from rust-lang/rfcs#574, tracking issue #23055. This is a big step forward for vector usability. This is an introduction of an API for removing a range of *m* consecutive elements from a vector, as efficently as possible. New features: - Introduce trait `std::collections::range::RangeArgument` implemented by all four built-in range types. - Change `Vec::drain()` to use `Vec::drain<R: RangeArgument>(R)` Implementation notes: - Use @gankro's idea for memory safety: Use `set_len` on the source vector when creating the iterator, to make sure that the part of the vector that will be modified is unreachable. Fix up things in Drain's destructor — but even if it doesn't run, we don't expose any moved-out-from slots of the vector. - This `.drain<R>(R)` very close to how it is specified in the RFC. - Introduced as unstable - Drain reuses the slice iterator — copying and pasting the same iterator pointer arithmetic again felt very bad - The `usize` index as a range argument in the RFC is not included. The ranges trait would have to change to accomodate it. Please help me with: - Name and location of the new ranges trait. - Design of the ranges trait - Understanding Niko's comments about variance (Note: for a long time I was using a straight up &mut Vec in the iterator, but I changed this to permit reusing the slice iterator). Previous PR and discussion: #23071
2 parents cadc67e + b475fc7 commit 8871c17

File tree

8 files changed

+174
-85
lines changed

8 files changed

+174
-85
lines changed

src/libcollections/binary_heap.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ impl<T: Ord> BinaryHeap<T> {
546546
#[unstable(feature = "collections",
547547
reason = "matches collection reform specification, waiting for dust to settle")]
548548
pub fn drain(&mut self) -> Drain<T> {
549-
Drain { iter: self.data.drain() }
549+
Drain { iter: self.data.drain(..) }
550550
}
551551

552552
/// Drops all items from the binary heap.

src/libcollections/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@
4242
#![feature(slice_patterns)]
4343
#![feature(debug_builders)]
4444
#![feature(utf8_error)]
45-
#![cfg_attr(test, feature(rand, rustc_private, test, hash, collections))]
45+
#![cfg_attr(test, feature(rand, rustc_private, test, hash, collections,
46+
collections_drain, collections_range))]
4647
#![cfg_attr(test, allow(deprecated))] // rand
4748

4849
#![feature(no_std)]
@@ -82,6 +83,7 @@ pub mod borrow;
8283
pub mod enum_set;
8384
pub mod fmt;
8485
pub mod linked_list;
86+
pub mod range;
8587
pub mod slice;
8688
pub mod str;
8789
pub mod string;

src/libcollections/range.rs

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
#![unstable(feature = "collections_range", reason = "was just added")]
11+
12+
//! Range syntax.
13+
14+
use core::option::Option::{self, None, Some};
15+
use core::ops::{RangeFull, Range, RangeTo, RangeFrom};
16+
17+
/// **RangeArgument** is implemented by Rust's built-in range types, produced
18+
/// by range syntax like `..`, `a..`, `..b` or `c..d`.
19+
pub trait RangeArgument<T> {
20+
/// Start index (inclusive)
21+
///
22+
/// Return start value if present, else `None`.
23+
fn start(&self) -> Option<&T> { None }
24+
25+
/// End index (exclusive)
26+
///
27+
/// Return end value if present, else `None`.
28+
fn end(&self) -> Option<&T> { None }
29+
}
30+
31+
32+
impl<T> RangeArgument<T> for RangeFull {}
33+
34+
impl<T> RangeArgument<T> for RangeFrom<T> {
35+
fn start(&self) -> Option<&T> { Some(&self.start) }
36+
}
37+
38+
impl<T> RangeArgument<T> for RangeTo<T> {
39+
fn end(&self) -> Option<&T> { Some(&self.end) }
40+
}
41+
42+
impl<T> RangeArgument<T> for Range<T> {
43+
fn start(&self) -> Option<&T> { Some(&self.start) }
44+
fn end(&self) -> Option<&T> { Some(&self.end) }
45+
}

src/libcollections/vec.rs

+87-78
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ use core::usize;
6969

7070
use borrow::{Cow, IntoCow};
7171

72+
use super::range::RangeArgument;
73+
7274
// FIXME- fix places which assume the max vector allowed has memory usize::MAX.
7375
static MAX_MEMORY_SIZE: usize = isize::MAX as usize;
7476

@@ -714,36 +716,61 @@ impl<T> Vec<T> {
714716
unsafe { other.set_len(0); }
715717
}
716718

717-
/// Creates a draining iterator that clears the `Vec` and iterates over
718-
/// the removed items from start to end.
719+
/// Create a draining iterator that removes the specified range in the vector
720+
/// and yields the removed items from start to end. The element range is
721+
/// removed even if the iterator is not consumed until the end.
722+
///
723+
/// Note: It is unspecified how many elements are removed from the vector,
724+
/// if the `Drain` value is leaked.
725+
///
726+
/// # Panics
727+
///
728+
/// Panics if the starting point is greater than the end point or if
729+
/// the end point is greater than the length of the vector.
719730
///
720731
/// # Examples
721732
///
722733
/// ```
723-
/// # #![feature(collections)]
724-
/// let mut v = vec!["a".to_string(), "b".to_string()];
725-
/// for s in v.drain() {
726-
/// // s has type String, not &String
727-
/// println!("{}", s);
728-
/// }
729-
/// assert!(v.is_empty());
734+
/// # #![feature(collections_drain, collections_range)]
735+
///
736+
/// // Draining using `..` clears the whole vector.
737+
/// let mut v = vec![1, 2, 3];
738+
/// let u: Vec<_> = v.drain(..).collect();
739+
/// assert_eq!(v, &[]);
740+
/// assert_eq!(u, &[1, 2, 3]);
730741
/// ```
731-
#[inline]
732-
#[unstable(feature = "collections",
733-
reason = "matches collection reform specification, waiting for dust to settle")]
734-
pub fn drain(&mut self) -> Drain<T> {
742+
#[unstable(feature = "collections_drain",
743+
reason = "recently added, matches RFC")]
744+
pub fn drain<R>(&mut self, range: R) -> Drain<T> where R: RangeArgument<usize> {
745+
// Memory safety
746+
//
747+
// When the Drain is first created, it shortens the length of
748+
// the source vector to make sure no uninitalized or moved-from elements
749+
// are accessible at all if the Drain's destructor never gets to run.
750+
//
751+
// Drain will ptr::read out the values to remove.
752+
// When finished, remaining tail of the vec is copied back to cover
753+
// the hole, and the vector length is restored to the new length.
754+
//
755+
let len = self.len();
756+
let start = *range.start().unwrap_or(&0);
757+
let end = *range.end().unwrap_or(&len);
758+
assert!(start <= end);
759+
assert!(end <= len);
760+
735761
unsafe {
736-
let begin = *self.ptr as *const T;
737-
let end = if mem::size_of::<T>() == 0 {
738-
(*self.ptr as usize + self.len()) as *const T
739-
} else {
740-
(*self.ptr).offset(self.len() as isize) as *const T
741-
};
742-
self.set_len(0);
762+
// set self.vec length's to start, to be safe in case Drain is leaked
763+
self.set_len(start);
764+
// Use the borrow in the IterMut to indicate borrowing behavior of the
765+
// whole Drain iterator (like &mut T).
766+
let range_slice = slice::from_raw_parts_mut(
767+
self.as_mut_ptr().offset(start as isize),
768+
end - start);
743769
Drain {
744-
ptr: begin,
745-
end: end,
746-
marker: PhantomData,
770+
tail_start: end,
771+
tail_len: len - end,
772+
iter: range_slice.iter_mut(),
773+
vec: self as *mut _,
747774
}
748775
}
749776
}
@@ -1795,14 +1822,16 @@ impl<T> Drop for IntoIter<T> {
17951822
}
17961823
}
17971824

1798-
/// An iterator that drains a vector.
1799-
#[unsafe_no_drop_flag]
1800-
#[unstable(feature = "collections",
1801-
reason = "recently added as part of collections reform 2")]
1802-
pub struct Drain<'a, T:'a> {
1803-
ptr: *const T,
1804-
end: *const T,
1805-
marker: PhantomData<&'a T>,
1825+
/// A draining iterator for `Vec<T>`.
1826+
#[unstable(feature = "collections_drain", reason = "recently added")]
1827+
pub struct Drain<'a, T: 'a> {
1828+
/// Index of tail to preserve
1829+
tail_start: usize,
1830+
/// Length of tail
1831+
tail_len: usize,
1832+
/// Current remaining range to remove
1833+
iter: slice::IterMut<'a, T>,
1834+
vec: *mut Vec<T>,
18061835
}
18071836

18081837
unsafe impl<'a, T: Sync> Sync for Drain<'a, T> {}
@@ -1814,76 +1843,56 @@ impl<'a, T> Iterator for Drain<'a, T> {
18141843

18151844
#[inline]
18161845
fn next(&mut self) -> Option<T> {
1817-
unsafe {
1818-
if self.ptr == self.end {
1819-
None
1820-
} else {
1821-
if mem::size_of::<T>() == 0 {
1822-
// purposefully don't use 'ptr.offset' because for
1823-
// vectors with 0-size elements this would return the
1824-
// same pointer.
1825-
self.ptr = mem::transmute(self.ptr as usize + 1);
1826-
1827-
// Use a non-null pointer value
1828-
Some(ptr::read(EMPTY as *mut T))
1829-
} else {
1830-
let old = self.ptr;
1831-
self.ptr = self.ptr.offset(1);
1832-
1833-
Some(ptr::read(old))
1834-
}
1846+
self.iter.next().map(|elt|
1847+
unsafe {
1848+
ptr::read(elt as *const _)
18351849
}
1836-
}
1850+
)
18371851
}
18381852

1839-
#[inline]
18401853
fn size_hint(&self) -> (usize, Option<usize>) {
1841-
let diff = (self.end as usize) - (self.ptr as usize);
1842-
let size = mem::size_of::<T>();
1843-
let exact = diff / (if size == 0 {1} else {size});
1844-
(exact, Some(exact))
1854+
self.iter.size_hint()
18451855
}
18461856
}
18471857

18481858
#[stable(feature = "rust1", since = "1.0.0")]
18491859
impl<'a, T> DoubleEndedIterator for Drain<'a, T> {
18501860
#[inline]
18511861
fn next_back(&mut self) -> Option<T> {
1852-
unsafe {
1853-
if self.end == self.ptr {
1854-
None
1855-
} else {
1856-
if mem::size_of::<T>() == 0 {
1857-
// See above for why 'ptr.offset' isn't used
1858-
self.end = mem::transmute(self.end as usize - 1);
1859-
1860-
// Use a non-null pointer value
1861-
Some(ptr::read(EMPTY as *mut T))
1862-
} else {
1863-
self.end = self.end.offset(-1);
1864-
1865-
Some(ptr::read(self.end))
1866-
}
1862+
self.iter.next_back().map(|elt|
1863+
unsafe {
1864+
ptr::read(elt as *const _)
18671865
}
1868-
}
1866+
)
18691867
}
18701868
}
18711869

1872-
#[stable(feature = "rust1", since = "1.0.0")]
1873-
impl<'a, T> ExactSizeIterator for Drain<'a, T> {}
1874-
18751870
#[unsafe_destructor]
18761871
#[stable(feature = "rust1", since = "1.0.0")]
18771872
impl<'a, T> Drop for Drain<'a, T> {
18781873
fn drop(&mut self) {
1879-
// self.ptr == self.end == mem::POST_DROP_USIZE if drop has already been called,
1880-
// so we can use #[unsafe_no_drop_flag].
1874+
// exhaust self first
1875+
while let Some(_) = self.next() { }
18811876

1882-
// destroy the remaining elements
1883-
for _x in self.by_ref() {}
1877+
if self.tail_len > 0 {
1878+
unsafe {
1879+
let source_vec = &mut *self.vec;
1880+
// memmove back untouched tail, update to new length
1881+
let start = source_vec.len();
1882+
let tail = self.tail_start;
1883+
let src = source_vec.as_ptr().offset(tail as isize);
1884+
let dst = source_vec.as_mut_ptr().offset(start as isize);
1885+
ptr::copy(src, dst, self.tail_len);
1886+
source_vec.set_len(start + self.tail_len);
1887+
}
1888+
}
18841889
}
18851890
}
18861891

1892+
1893+
#[stable(feature = "rust1", since = "1.0.0")]
1894+
impl<'a, T> ExactSizeIterator for Drain<'a, T> {}
1895+
18871896
////////////////////////////////////////////////////////////////////////////////
18881897
// Conversion from &[T] to &Vec<T>
18891898
////////////////////////////////////////////////////////////////////////////////

src/libcollections/vec_map.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ impl<V> VecMap<V> {
418418
}
419419
let filter: fn((usize, Option<V>)) -> Option<(usize, V)> = filter; // coerce to fn ptr
420420

421-
Drain { iter: self.v.drain().enumerate().filter_map(filter) }
421+
Drain { iter: self.v.drain(..).enumerate().filter_map(filter) }
422422
}
423423

424424
/// Returns the number of elements in the map.

src/libcollectionstest/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#![feature(box_syntax)]
1212
#![feature(collections)]
13+
#![feature(collections_drain)]
1314
#![feature(core)]
1415
#![feature(hash)]
1516
#![feature(rand)]

src/libcollectionstest/vec.rs

+33-3
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ fn test_move_items_zero_sized() {
460460
fn test_drain_items() {
461461
let mut vec = vec![1, 2, 3];
462462
let mut vec2 = vec![];
463-
for i in vec.drain() {
463+
for i in vec.drain(..) {
464464
vec2.push(i);
465465
}
466466
assert_eq!(vec, []);
@@ -471,7 +471,7 @@ fn test_drain_items() {
471471
fn test_drain_items_reverse() {
472472
let mut vec = vec![1, 2, 3];
473473
let mut vec2 = vec![];
474-
for i in vec.drain().rev() {
474+
for i in vec.drain(..).rev() {
475475
vec2.push(i);
476476
}
477477
assert_eq!(vec, []);
@@ -482,13 +482,43 @@ fn test_drain_items_reverse() {
482482
fn test_drain_items_zero_sized() {
483483
let mut vec = vec![(), (), ()];
484484
let mut vec2 = vec![];
485-
for i in vec.drain() {
485+
for i in vec.drain(..) {
486486
vec2.push(i);
487487
}
488488
assert_eq!(vec, []);
489489
assert_eq!(vec2, [(), (), ()]);
490490
}
491491

492+
#[test]
493+
#[should_panic]
494+
fn test_drain_out_of_bounds() {
495+
let mut v = vec![1, 2, 3, 4, 5];
496+
v.drain(5..6);
497+
}
498+
499+
#[test]
500+
fn test_drain_range() {
501+
let mut v = vec![1, 2, 3, 4, 5];
502+
for _ in v.drain(4..) {
503+
}
504+
assert_eq!(v, &[1, 2, 3, 4]);
505+
506+
let mut v: Vec<_> = (1..6).map(|x| x.to_string()).collect();
507+
for _ in v.drain(1..4) {
508+
}
509+
assert_eq!(v, &[1.to_string(), 5.to_string()]);
510+
511+
let mut v: Vec<_> = (1..6).map(|x| x.to_string()).collect();
512+
for _ in v.drain(1..4).rev() {
513+
}
514+
assert_eq!(v, &[1.to_string(), 5.to_string()]);
515+
516+
let mut v: Vec<_> = vec![(); 5];
517+
for _ in v.drain(1..4).rev() {
518+
}
519+
assert_eq!(v, &[(), ()]);
520+
}
521+
492522
#[test]
493523
fn test_into_boxed_slice() {
494524
let xs = vec![1, 2, 3];

src/test/run-pass/sync-send-iterators-in-libcollections.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#![allow(unused_mut)]
1414
#![feature(collections)]
15+
#![feature(collections_drain)]
1516

1617
extern crate collections;
1718

@@ -96,5 +97,6 @@ fn main() {
9697

9798
all_sync_send!(VecMap::<usize>::new(), iter, iter_mut, drain, into_iter, keys, values);
9899

99-
all_sync_send!(Vec::<usize>::new(), into_iter, drain);
100+
all_sync_send!(Vec::<usize>::new(), into_iter);
101+
is_sync_send!(Vec::<usize>::new(), drain(..));
100102
}

0 commit comments

Comments
 (0)