可选地在自定义迭代器“next()”函数中调用“skip”

问题描述

我有一个自定义迭代器,我想在自定义 .skip(...) 方法中选择性地调用 .next()。但是,我收到类型错误,因为 Skip != Iterator

示例代码如下:

struct CrossingIter<'a,T> {
    index: usize,iter: std::slice::Iter<'a,T>,}

impl<'a,T: Float> Iterator for CrossingIter<'a,T> {
    type Item = (usize,T);


    fn next(&mut self) -> Option<(usize,T)> {
        let iter = (&mut self.iter).enumerate();
         
        let iter = if self.index == 0 {
            self.index += 3;
            iter.skip(3)
        } else {
            iter
        }

        // lots of code here working with the new iterator        

        iter.next()
    }
}

问题是调用 .skip(3) 后,iter 的类型发生了变化。一种解决方案是在 // lots of code ... 语句的每个分支中复制 if,但我不想这样做。

我的问题是:有没有办法有条件地将 skip(...) 应用于迭代器并继续使用它而不复制一堆代码

解决方法

skip 旨在构造一个新的迭代器,这在您希望代码至少在表面上保持不变的情况下非常有用。但是,在您的情况下,您希望推进现有 迭代器,同时仍保持其有效。

advance_by 可以做你想做的事,但它是 Nightly,所以它不会在稳定的 Rust 上运行。

if self.index == 0 {
  self.index += 3;
  self.iter.advance_by(3);
}

我们可以滥用 nth 来获得我们想要的东西,但这不是很惯用。

if self.index == 0 {
  self.index += 3;
  self.iter.nth(2);
}

如果我在生产环境中看到该代码,我会很困惑。

最简单且不太令人满意的答案是将 advance_by 重新实现为辅助函数。 The source 可用且很容易适应

fn my_advance_by(iter: &mut impl Iterator,n: usize) -> Result<(),usize> {
  for i in 0..n {
    iter.next().ok_or(i)?;
  }
  Ok(())
}

所有这一切,如果您的用例实际上只是跳过前三个元素,您需要的只是从 skip 调用开始并假设您的迭代器始终是 { {1}}

Skip
,

我认为@Silvio 的回答是一个更好的视角。


您可以在 skip(0) 分支中调用 iter 而不是 else 本身...

并且 enumerate 生成的迭代器的返回值与您的定义不匹配:fn next(&mut self) -> Option<(usize,T)>。你需要映射它。

这是一个工作示例:

use num::Float;

struct CrossingIter<'a,T> {
    index: usize,iter: std::slice::Iter<'a,T>,}

impl<'a,T: Float> Iterator for CrossingIter<'a,T> {
    type Item = (usize,T);


    fn next(&mut self) -> Option<(usize,T)> {
        let iter = (&mut self.iter).enumerate();
         
        let mut iter = if self.index == 0 {
            self.index += 3;
            iter.skip(3)
        } else {
            iter.skip(0)
        };

        // lots of code here working with the new iterator        

        iter.next().map(|(i,&v)| (i,v))
    }
}