IntoIter
让我们继续编写迭代器。由于 Deref 的魔力,iter
和 iter_mut
已经为我们写好了。但是,Vec 提供了两个切片无法提供的有趣迭代器:into_iter
和 drain
。
IntoIter 通过值消耗 Vec,因此可以通过值产生其元素。为了实现这一点,IntoIter 需要控制 Vec 的分配。
IntoIter 也需要是 DoubleEnded 的,以便能够从两端读取。从后面读取可以简单地通过调用 pop
来实现,但从前面读取则比较困难。我们可以调用 remove(0)
,但这将非常昂贵。相反,我们将只使用 ptr::read 从 Vec 的任一端复制值,而完全不改变缓冲区。
为此,我们将使用一种非常常见的 C 语言数组迭代习惯用法。我们将创建两个指针;一个指向数组的开头,另一个指向数组末尾的后一个元素。当我们想要从一端获取元素时,我们将读取该端指向的值,并将指针移动一位。当两个指针相等时,我们就知道迭代结束了。
请注意,读取和偏移的顺序对于 next
和 next_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);
}
}
}
}