| # Drain |
| |
| Let's move on to Drain. Drain is largely the same as IntoIter, except that |
| instead of consuming the Vec, it borrows the Vec and leaves its allocation |
| untouched. For now we'll only implement the "basic" full-range version. |
| |
| <!-- ignore: simplified code --> |
| ```rust,ignore |
| use std::marker::PhantomData; |
| |
| struct Drain<'a, T: 'a> { |
| // Need to bound the lifetime here, so we do it with `&'a mut Vec<T>` |
| // because that's semantically what we contain. We're "just" calling |
| // `pop()` and `remove(0)`. |
| vec: PhantomData<&'a mut Vec<T>>, |
| start: *const T, |
| end: *const T, |
| } |
| |
| impl<'a, T> Iterator for Drain<'a, T> { |
| type Item = T; |
| fn next(&mut self) -> Option<T> { |
| if self.start == self.end { |
| None |
| ``` |
| |
| -- wait, this is seeming familiar. Let's do some more compression. Both |
| IntoIter and Drain have the exact same structure, let's just factor it out. |
| |
| <!-- ignore: simplified code --> |
| ```rust,ignore |
| struct RawValIter<T> { |
| start: *const T, |
| end: *const T, |
| } |
| |
| impl<T> RawValIter<T> { |
| // unsafe to construct because it has no associated lifetimes. |
| // This is necessary to store a RawValIter in the same struct as |
| // its actual allocation. OK since it's a private implementation |
| // detail. |
| unsafe fn new(slice: &[T]) -> Self { |
| RawValIter { |
| start: slice.as_ptr(), |
| end: if slice.len() == 0 { |
| // if `len = 0`, then this is not actually allocated memory. |
| // Need to avoid offsetting because that will give wrong |
| // information to LLVM via GEP. |
| slice.as_ptr() |
| } else { |
| slice.as_ptr().add(slice.len()) |
| } |
| } |
| } |
| } |
| |
| // Iterator and DoubleEndedIterator impls identical to IntoIter. |
| ``` |
| |
| And IntoIter becomes the following: |
| |
| <!-- ignore: simplified code --> |
| ```rust,ignore |
| pub struct IntoIter<T> { |
| _buf: RawVec<T>, // we don't actually care about this. Just need it to live. |
| iter: RawValIter<T>, |
| } |
| |
| impl<T> Iterator for IntoIter<T> { |
| type Item = T; |
| fn next(&mut self) -> Option<T> { self.iter.next() } |
| fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } |
| } |
| |
| impl<T> DoubleEndedIterator for IntoIter<T> { |
| fn next_back(&mut self) -> Option<T> { self.iter.next_back() } |
| } |
| |
| impl<T> Drop for IntoIter<T> { |
| fn drop(&mut self) { |
| for _ in &mut *self {} |
| } |
| } |
| |
| impl<T> IntoIterator for Vec<T> { |
| type Item = T; |
| type IntoIter = IntoIter<T>; |
| fn into_iter(self) -> IntoIter<T> { |
| unsafe { |
| let iter = RawValIter::new(&self); |
| |
| let buf = ptr::read(&self.buf); |
| mem::forget(self); |
| |
| IntoIter { |
| iter, |
| _buf: buf, |
| } |
| } |
| } |
| } |
| ``` |
| |
| Note that I've left a few quirks in this design to make upgrading Drain to work |
| with arbitrary subranges a bit easier. In particular we *could* have RawValIter |
| drain itself on drop, but that won't work right for a more complex Drain. |
| We also take a slice to simplify Drain initialization. |
| |
| Alright, now Drain is really easy: |
| |
| <!-- ignore: simplified code --> |
| ```rust,ignore |
| use std::marker::PhantomData; |
| |
| pub struct Drain<'a, T: 'a> { |
| vec: PhantomData<&'a mut Vec<T>>, |
| iter: RawValIter<T>, |
| } |
| |
| impl<'a, T> Iterator for Drain<'a, T> { |
| type Item = T; |
| fn next(&mut self) -> Option<T> { self.iter.next() } |
| fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() } |
| } |
| |
| impl<'a, T> DoubleEndedIterator for Drain<'a, T> { |
| fn next_back(&mut self) -> Option<T> { self.iter.next_back() } |
| } |
| |
| impl<'a, T> Drop for Drain<'a, T> { |
| fn drop(&mut self) { |
| for _ in &mut *self {} |
| } |
| } |
| |
| impl<T> Vec<T> { |
| pub fn drain(&mut self) -> Drain<T> { |
| let iter = unsafe { RawValIter::new(&self) }; |
| |
| // this is a mem::forget safety thing. If Drain is forgotten, we just |
| // leak the whole Vec's contents. Also we need to do this *eventually* |
| // anyway, so why not do it now? |
| self.len = 0; |
| |
| Drain { |
| iter, |
| vec: PhantomData, |
| } |
| } |
| } |
| ``` |
| |
| For more details on the `mem::forget` problem, see the |
| [section on leaks][leaks]. |
| |
| [leaks]: ../leaking.html |