原文链接:https://doc.rust-lang.org/nomicon/vec-drain.html

Drain

我们接着看看Drain。Drain和IntoIter基本相同,只不过它并不获取Vec的值,而是借用Vec并且不改变它的分配空间。现在我们只是先最“基本”的全范围(full-range)的版本。

  1. use std::marker::PhantomData;
  2. struct Drain<'a, T: 'a> {
  3. // 这里需要限制生命周期。我们使用&'a mut Vec<T>,因为这就是语义上我们包含的东西。
  4. // 我们只调用pop()和remove(0)
  5. vec: PhantomData<&'a mut Vec<T>>,
  6. start: *const T,
  7. end: *const T,
  8. }
  9. impl<'a, T> Iterator for Drain<'a, T> {
  10. type Item = T;
  11. fn next(&mut self) -> Option<T> {
  12. if self.start == self.end {
  13. None

——等一下,这个看着有点眼熟。我们需要做进一步的压缩。IntoIter和Drain有着完全一样的结构,我们把它提取出来。

  1. struct RawValIter<T> {
  2. start: *const T,
  3. end: *const T,
  4. }
  5. impl<T> RawValIter<T> {
  6. // 构建它是非安全的,因为它没有关联的生命周期。
  7. unsafe fn new(slice: &[T]) -> Self {
  8. RawValIter {
  9. start: slice.as_ptr(),
  10. end: if slice.len() == 0 {
  11. // 如果len == 0,说明没有真的分配内存。这时需要避免offset,
  12. // 因为那会给LLVM的GEP提供错误的信息
  13. slice.as_ptr()
  14. } else {
  15. slice.as_ptr().offset(slice.len() as isize)
  16. }
  17. }
  18. }
  19. }
  20. // Iterator和DoubleEndedIterator的实现与IntoIter完全一样。

IntoIter变成了这样:

  1. pub struct IntoIter<T> {
  2. _buf: RawVec<T>, // 我们并不关心这个,只是需要它们保持分配空间不被销毁
  3. iter: RawValIter<T>,
  4. }
  5. impl<T> Iterator for IntoIter<T> {
  6. type Item = T;
  7. fn next(&mut self) -> Option<T> { self.iter.next() }
  8. fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
  9. }
  10. impl<T> DoubleEndedIterator for IntoIter<T> {
  11. fn next_back(&mut self) -> Option<T> { self.iter.next_back() }
  12. }
  13. impl<T> Drop for IntoIter<T> {
  14. fn drop(&mut self) {
  15. for _ in &mut self.iter {}
  16. }
  17. }
  18. impl<T> Vec<T> {
  19. pub fn into_iter(self) -> IntoIter<T> {
  20. unsafe {
  21. let iter = RawValIter::new(&self);
  22. let buf = ptr::read(&self.buf);
  23. mem::forget(self);
  24. IntoIter {
  25. iter: iter,
  26. _buf: buf,
  27. }
  28. }
  29. }
  30. }

注意,我在设计中留下了一些小后门,以便更简单地将Drain升级为可访问任意子范围的版本。特别是,我们可以在drop中让RawValIter遍历它自己。但是这种设计不适用于更复杂的Drain。我们还使用一个slice简化Drain的初始化。

好了,现在Drain变得很简单:

  1. use std::marker::PhantomData;
  2. pub struct Drain<'a, T: 'a> {
  3. vec: PhantomData<&'a mut Vec<T>>,
  4. iter: RawValIter<T>,
  5. }
  6. impl<'a, T> Iterator for Drain<'a, T> {
  7. type Item = T;
  8. fn next(&mut self) -> Option<T> { self.iter.next() }
  9. fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
  10. }
  11. impl<'a, T> DoubleEndedIterator for Drain<'a, T> {
  12. fn next_back(&mut self) -> Option<T> { self.iter.next_back() }
  13. }
  14. impl<'a, T> Drop for Drain<'a, T> {
  15. fn drop(&mut self) {
  16. for _ in &mut self.iter {}
  17. }
  18. }
  19. impl<T> Vec<T> {
  20. pub fn drain(&mut self) -> Drain<T> {
  21. unsafe {
  22. let iter = RawValIter::new(&self);
  23. // 这一步是为了mem::forget的安全。如果Drain被forget,我们会泄露整个Vec的内容
  24. // 同时,既然我们无论如何都会做这一步,为什么不现在做呢?
  25. self.len = 0;
  26. Drain {
  27. iter: iter,
  28. vec: PhantomData,
  29. }
  30. }
  31. }
  32. }

关于更多的mem::forget的问题,请见关于泄露的章节