【问题标题】:How to implement an iterator over chunks of an array in a struct?如何在结构中的数组块上实现迭代器?
【发布时间】:2019-11-03 22:22:41
【问题描述】:

我想为结构实现一个迭代器,并将数组作为其字段之一。迭代器应该返回该数组的一个切片,但这需要一个生命周期参数。那个参数应该去哪里?

Rust 版本是 1.37.0

struct A {
    a: [u8; 100],
    num: usize,
}

impl Iterator for A {
    type Item = &[u8]; // this requires a lifetime parameter, but there is none declared

    fn next(&mut self) -> Option<Self::Item> {
         if self.num >= 10 {
             return None;
         }

         let res = &self.a[10*self.num..10*(self.num+1)];
         self.num += 1;
         Some(res)
    }
}

【问题讨论】:

    标签: rust iterator lifetime


    【解决方案1】:

    我不会自己实现。相反,我会重用现有的 chunks 迭代器和 implement IntoIterator for a reference to the type

    struct A {
        a: [u8; 100],
        num: usize,
    }
    
    impl<'a> IntoIterator for &'a A {
        type Item = &'a [u8];
        type IntoIter = std::slice::Chunks<'a, u8>;
    
        fn into_iter(self) -> Self::IntoIter {
            self.a.chunks(self.num)
        }
    }
    
    fn example(a: A) {
        for chunk in &a {
            println!("{}", chunk.iter().sum::<u8>())
        }
    }
    

    【讨论】:

      【解决方案2】:

      当你从一个函数返回一个引用时,它的生命周期需要与其他东西联系起来。否则,编译器将不知道引用的有效时间(例外情况是 'static 生命周期,它会持续整个程序的持续时间)。

      所以我们需要对切片的现有引用。执行此操作的一种标准方法是将引用绑定到迭代器本身。例如,

      struct Iter<'a> {
          slice: &'a [u8; 100],
          num: usize,
      }
      

      那么你所拥有的几乎是逐字逐句的。 (我更改了类型和字段的名称以提供更多信息)。

      impl<'a> Iterator for Iter<'a> {
          type Item = &'a [u8];
      
          fn next(&mut self) -> Option<Self::Item> {
              if self.num >= 100 {
                  return None;
              }
      
              let res = &self.slice[10 * self.num..10 * (self.num + 1)];
              self.num += 1;
              Some(res)
          }
      }
      

      现在,您可能在某处仍有实际的[u8; 100],而不仅仅是参考。如果您仍想使用它,您将需要一个单独的结构,该结构具有转换为A 的方法。例如

      struct Data {
          array: [u8; 100],
      }
      
      impl Data {
          fn iter<'a>(&'a self) -> Iter<'a> {
              Iter {
                  slice: &self.array,
                  num: 0,
              }
          }
      }
      

      由于生命周期省略,iter 的生命周期可以省略:

      impl Data {
          fn iter(&self) -> Iter {
              Iter {
                  slice: &self.array,
                  num: 0,
              }
          }
      }
      

      (playground)

      只是一些笔记。 [0u8; 100] 存在一个编译器错误。这可能是[u8; 100] 的拼写错误,但以防万一,这就是我们不能这样做的原因。在结构定义的字段中,仅指定类型。字段或类似的东西没有默认值。如果您尝试为结构设置默认值,请考虑使用Default trait

      其次,您可能已经意识到这一点,但已经有 an implementation 的切片块迭代器。如果slice 是一个切片(或者可以取消引用强制转换为一个切片- 向量和数组是主要示例),那么slice.chunks(n) 是该切片的块的迭代器,长度为n。我在上面链接的代码中给出了一个例子。有趣的是,该实现使用了一个非常相似的想法: slice.chunks(n) 返回一个带有生命周期参数的新结构并实现Iterator。这和我们的Data::iter几乎一模一样。

      最后,您的next 实现中有一个错误,在运行时会导致越界恐慌。看看你能不能发现它!

      【讨论】:

      • 感谢您详尽而完整的回答。我非常感谢。我已经编辑了问题以消除提到的错误。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-25
      • 2021-09-17
      • 2016-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多