IntoIter

让我们继续编写迭代器。由于 Deref 的魔力,iteriter_mut 已经为我们编写好了。然而,Vec 提供了两个切片无法提供的有趣迭代器:into_iterdrain

IntoIter 通过值消耗 Vec,因此可以按值产生其元素。为了实现这一点,IntoIter 需要控制 Vec 的分配。

IntoIter 也需要是 DoubleEnded 的,以便能够从两端读取。从后面读取可以直接实现为调用 pop,但是从前面读取则更困难。我们可以调用 remove(0),但这会非常昂贵。相反,我们将只使用 ptr::read 从 Vec 的任一端复制值,而不会改变缓冲区。

为此,我们将使用一种非常常见的 C 语言数组迭代习惯用法。我们将创建两个指针;一个指向数组的开头,另一个指向数组末尾之后的一个元素。当我们想要从一端获取一个元素时,我们将读取该端指向的值,并将指针移动一位。当两个指针相等时,我们知道我们已经完成了。

请注意,对于 nextnext_back,读取和偏移的顺序是相反的。对于 next_back,指针始终在它想要读取的下一个元素之后,而对于 next,指针始终位于它想要读取的下一个元素。要了解为什么会这样,请考虑除了一个元素之外所有元素都已产生的情况。

数组看起来像这样

          S  E
[X, X, X, O, X, X, X]

如果 E 直接指向它想要产生的下一个元素,那么它将无法与没有更多元素可产生的情况区分开来。

虽然我们在迭代过程中实际上并不关心它,但我们还需要保存 Vec 的分配信息,以便在 IntoIter 被丢弃后释放它。

因此,我们将使用以下结构体

pub struct IntoIter<T> {
    buf: NonNull<T>,
    cap: usize,
    start: *const T,
    end: *const T,
}

这就是我们最终得到的初始化结果

impl<T> IntoIterator for Vec<T> {
    type Item = T;
    type IntoIter = IntoIter<T>;
    fn into_iter(self) -> IntoIter<T> {
        // Make sure not to drop Vec since that would free the buffer
        let vec = ManuallyDrop::new(self);

        // Can't destructure Vec since it's Drop
        let ptr = vec.ptr;
        let cap = vec.cap;
        let len = vec.len;

        IntoIter {
            buf: ptr,
            cap,
            start: ptr.as_ptr(),
            end: if cap == 0 {
                // can't offset off this pointer, it's not allocated!
                ptr.as_ptr()
            } else {
                unsafe { ptr.as_ptr().add(len) }
            },
        }
    }
}

这是向前迭代

impl<T> Iterator for IntoIter<T> {
    type Item = T;
    fn next(&mut self) -> Option<T> {
        if self.start == self.end {
            None
        } else {
            unsafe {
                let result = ptr::read(self.start);
                self.start = self.start.offset(1);
                Some(result)
            }
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let len = (self.end as usize - self.start as usize)
                  / mem::size_of::<T>();
        (len, Some(len))
    }
}

这是向后迭代。

impl<T> DoubleEndedIterator for IntoIter<T> {
    fn next_back(&mut self) -> Option<T> {
        if self.start == self.end {
            None
        } else {
            unsafe {
                self.end = self.end.offset(-1);
                Some(ptr::read(self.end))
            }
        }
    }
}

由于 IntoIter 获取了其分配的所有权,因此它需要实现 Drop 来释放它。但是它也希望实现 Drop 来丢弃其中包含的任何未产生的元素。

impl<T> Drop for IntoIter<T> {
    fn drop(&mut self) {
        if self.cap != 0 {
            // drop any remaining elements
            for _ in &mut *self {}
            let layout = Layout::array::<T>(self.cap).unwrap();
            unsafe {
                alloc::dealloc(self.buf.as_ptr() as *mut u8, layout);
            }
        }
    }
}