【发布时间】:2022-01-13 03:13:46
【问题描述】:
我正在关注guide 使用NonNull 实现调整大小的数组堆栈:
pub struct ResizingStack<T> {
a: NonNull<T>,
n: usize,
capacity: usize,
}
现在基本功能(例如,push 和 pop)运行良好。完整代码可以在here 找到。但是我在实现Iterator trait 时遇到了一些麻烦。
对于 forward 迭代器,一个简单的解决方案是使 ResizingStack 强制转换为 slice,并且其行为类似于 slice。
impl<T> Deref for Vec<T> {
type Target = [T];
fn deref(&self) -> &[T] {
unsafe {
std::slice::from_raw_parts(self.a.as_ptr(), self.n)
}
}
}
然而,事实上,一个栈应该有一个backward迭代器。以下是我的尝试:
pub struct StackIter<'a, T> {
buf: &'a ResizingStack<T>,
index: usize,
}
impl<T> ResizingStack<T> {
pub fn iter(&self) -> StackIter<'_, T> {
StackIter {
buf: self,
index: self.n,
}
}
}
impl<'a, T> Iterator for StackIter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
if self.index == 0 {
None
} else {
let item;
unsafe {
item = Some(ptr::read(self.buf.a.as_ptr().add(self.index - 1)));
self.index -= 1;
}
item // ERROR: expected `Option<&T>`, but found `Option<T>`.
}
}
}
【问题讨论】:
-
问题是
type Item = &'a T;承诺返回references,但ptr::read返回实际的值。 -
类型问题的直接解决方法是将
item = Some(ptr::read(...))替换为item = Some(&*self.buf.a.as_ptr().add(self.index - 1))。但答案显示了实现相同目标的更好方法。 -
换句话说,
ptr::read()比名称所暗示的更具破坏性 - 它执行(在 Rust 术语中)称为 move 的操作,并且仅适用于您拥有底层对象(你做的),当你不打算再使用它时(在你的代码中不是这种情况)。例如,您可以使用ptr::read()来实现into_iter()或drain()。