Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 2 additions & 2 deletions src/libcollections/binary_heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,8 +553,8 @@ impl<T: Ord> BinaryHeap<T> {
#[inline]
#[unstable(feature = "collections",
reason = "matches collection reform specification, waiting for dust to settle")]
pub fn drain(&mut self) -> Drain<T> {
Drain { iter: self.data.drain() }
pub fn drain<'a>(&'a mut self) -> Drain<'a, T> {
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe the lifetimes can be elided here?

Drain { iter: self.data.drain(..) }
}

/// Drops all items from the binary heap.
Expand Down
128 changes: 126 additions & 2 deletions src/libcollections/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ use core::fmt;
use core::hash;
use core::iter::{IntoIterator, FromIterator};
use core::mem;
use core::ops::{self, Deref, Add, Index};
use core::ops::{self, Deref, Add, Index, Range, RangeTo, RangeFrom, RangeFull};
use core::ptr;
use core::raw::Slice as RawSlice;
use unicode::str as unicode_str;
use unicode::str::Utf16Item;

use borrow::{Cow, IntoCow};
use str::{self, CharRange, FromStr, Utf8Error};
use str::{self, CharRange, FromStr, Utf8Error, Chars};
use vec::{DerefVec, Vec, as_vec};

/// A growable string stored as a UTF-8 encoded buffer.
Expand Down Expand Up @@ -670,6 +670,122 @@ impl String {
pub fn clear(&mut self) {
self.vec.clear()
}

/// Creates a draining iterator that clears the specified range in the String
/// and iterates over the characters contained in the range.
///
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should clarify that it will backshift if necessary.

/// # Example
Copy link
Contributor

Choose a reason for hiding this comment

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

Also I thought the convention for Strings was that indices were always byte-wise?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They are.

///
/// ```
/// let mut s = "Hello World!".to_string();
/// let s2: String = s.drain(6..11).collect();
/// assert_eq!(s, "Hello !");
/// assert_eq!(s2, "World");
/// ```
///
/// # Panics
///
/// Panics if the range is decreasing, if the upper bound is larger than the
/// length of the String, or if the start and the end of the range don't lie on
/// character boundaries.
pub fn drain<'a, T: DrainRange>(&'a mut self, range: T) -> CharDrain<'a> {
range.drain(self)
}
}

/// A trait for draining a string.
///
/// See the documentation of `String::drain`.
pub trait DrainRange {
fn drain<'a>(&self, s: &'a mut String) -> CharDrain<'a>;
}

impl DrainRange for Range<usize> {
fn drain<'a>(&self, s: &'a mut String) -> CharDrain<'a> {
assert!(self.start <= self.end, "Range not increasing");
assert!(self.end <= s.len(), "Range out of bounds");
unsafe {
let slice = mem::transmute::<&str, &'static str>(&s[self.start..self.end]);
let tail = s.len() - self.end;
s.as_mut_vec().set_len(tail + self.start);
let ptr = s.as_mut_vec().as_mut_ptr();
CharDrain {
tail: tail,
start: ptr.offset(self.start as isize),
end: ptr.offset(self.end as isize),
chars: slice.chars(),
}
}
}
}

impl DrainRange for RangeFrom<usize> {
fn drain<'a>(&self, s: &'a mut String) -> CharDrain<'a> {
assert!(self.start <= s.len(), "Range out of bounds");
(self.start..s.len()).drain(s)
}
}

impl DrainRange for RangeTo<usize> {
fn drain<'a>(&self, s: &'a mut String) -> CharDrain<'a> {
(0..self.end).drain(s)
}
}

impl DrainRange for RangeFull {
fn drain<'a>(&self, s: &'a mut String) -> CharDrain<'a> {
(0..s.len()).drain(s)
}
}

impl DrainRange for usize {
fn drain<'a>(&self, s: &'a mut String) -> CharDrain<'a> {
(*self..*self+1).drain(s)
}
}

/// An iterator that drains part of string.
#[unsafe_no_drop_flag]
pub struct CharDrain<'a> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Convention dictates that this should be called Drain.

tail: usize,
start: *mut u8,
end: *mut u8,
chars: Chars<'a>,
}

unsafe impl<'a> Sync for CharDrain<'a> {}
unsafe impl<'a> Send for CharDrain<'a> {}

impl<'a> Iterator for CharDrain<'a> {
type Item = char;

#[inline]
fn next(&mut self) -> Option<char> {
self.chars.next()
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.chars.size_hint()
}
}

impl<'a> DoubleEndedIterator for CharDrain<'a> {
#[inline]
fn next_back(&mut self) -> Option<char> {
self.chars.next_back()
}
}

#[unsafe_destructor]
impl<'a> Drop for CharDrain<'a> {
fn drop(&mut self) {
// self.start == null if drop has already been called, so we can use
// #[unsafe_no_drop_flag].
if !self.start.is_null() {
unsafe { ptr::copy(self.start, self.end, self.tail); }
}
}
}

impl FromUtf8Error {
Expand Down Expand Up @@ -1442,4 +1558,12 @@ mod tests {
r
});
}

#[test]
fn test_drain() {
let mut s = "Hello World!".to_string();
let s2: String = s.drain(6..11).collect();
assert_eq!(s, "Hello !");
assert_eq!(s2, "World");
}
}
Loading