【问题标题】:How to implement `Iterator` for a resizing array stack using `NonNull`?如何使用`NonNull`为调整大小的数组堆栈实现`Iterator`?
【发布时间】:2022-01-13 03:13:46
【问题描述】:

我正在关注guide 使用NonNull 实现调整大小的数组堆栈:

pub struct ResizingStack<T> {
    a: NonNull<T>,
    n: usize,
    capacity: usize,
}

现在基本功能(例如,pushpop)运行良好。完整代码可以在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 = &amp;'a T; 承诺返回references,但ptr::read 返回实际的
  • 类型问题的直接解决方法是将item = Some(ptr::read(...)) 替换为item = Some(&amp;*self.buf.a.as_ptr().add(self.index - 1))。但答案显示了实现相同目标的更好方法。
  • 换句话说,ptr::read() 比名称所暗示的更具破坏性 - 它执行(在 Rust 术语中)称为 move 的操作,并且仅适用于您拥有底层对象(你做的),当你不打算再使用它时(在你的代码中不是这种情况)。例如,您可以使用ptr::read() 来实现into_iter()drain()

标签: rust iterator


【解决方案1】:

对于产生&amp;T 的迭代器,无需使用unsafe 代码。

这是一个使用.split_last()的解决方案:

pub struct StackIter<'a, T> {
    buf: &'a [T],
}

impl<'a, T> Iterator for StackIter<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<Self::Item> {
        let (last, remainder) = self.buf.split_last()?;
        self.buf = remainder;
        Some(last)
    }
}

或者,这是一个包装.rev()的解决方案:

use std::{iter::Rev, slice};

pub struct StackIter<'a, T> {
    inner: Rev<slice::Iter<'a, T>>,
}

impl<'a, T> Iterator for StackIter<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next()
    }
}

此时,我们不妨丢掉包装器,直接返回适配器:

impl<T> ResizingStack<T> {
    pub fn iter<'a>(&'a self) -> impl Iterator<Item = &'a T> {
        self.deref().iter().rev()
    }
}

【讨论】:

  • 你提供了三个解决方案,我真的很想给你三个赞:)
  • 如果您真的很感激并且找不到更多投票的方法,请将答案标记为“已接受”。 :) 这也会让其他人知道该问题的答案已被接受。
猜你喜欢
  • 2023-04-02
  • 2021-10-11
  • 2021-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-20
  • 2015-08-17
相关资源
最近更新 更多