简体   繁体   English

如何在结构中的数组块上实现迭代器?

[英]How to implement an iterator over chunks of an array in a struct?

I want to implement an iterator for the struct with an array as one of its fields.我想为结构实现一个迭代器,并将数组作为其字段之一。 The iterator should return a slice of that array, but this requires a lifetime parameter.迭代器应该返回该数组的一个切片,但这需要一个生命周期参数。 Where should that parameter go?那个参数 go 应该在哪里?

The Rust version is 1.37.0 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)
    }
}

When you return a reference from a function, its lifetime needs to be tied to something else.当您从 function 返回引用时,它的生命周期需要与其他东西相关联。 Otherwise, the compiler wouldn't know how long the reference is valid (the exception to this is a 'static lifetime, which lasts for the duration of the whole program).否则,编译器将不知道引用的有效时间(例外情况是'static生命周期”,它会持续整个程序的持续时间)。

So we need an existing reference to the slices.所以我们需要对切片的现有引用。 One standard way to do this is to tie the reference to the iterator itself.执行此操作的一种标准方法是将引用绑定到迭代器本身。 For example,例如,

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

Then what you have works almost verbatim.然后你所拥有的几乎是逐字逐句的。 (I've changed the names of the types and fields to be a little more informative). (我已更改类型和字段的名称以提供更多信息)。

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)
    }
}

Now, you probably still have an actual [u8; 100]现在,你可能还有一个实际的[u8; 100] [u8; 100] somewhere, not just a reference. [u8; 100]某处,而不仅仅是参考。 If you still want to work with that, what you'll want is a separate struct that has a method to convert into A .如果您仍想使用它,您将需要一个单独的结构,该结构具有转换为A的方法。 For example例如

struct Data {
    array: [u8; 100],
}

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

Thanks to lifetime elision, the lifetimes on iter can be left out:由于生命周期省略,可以省略iter上的生命周期:

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

(playground) (操场)

Just a few notes.只是一些笔记。 There was one compiler error with [0u8; 100] [0u8; 100] [0u8; 100] . [0u8; 100] This may have been a typo for [u8; 100]这可能是[u8; 100] [u8; 100] , but just in case, here's why we can't do that. [u8; 100] ,但以防万一,这就是我们不能这样做的原因。 In the fields for a struct definition, only the types are specified.在结构定义的字段中,仅指定类型。 There aren't default values for the fields or anything like that.字段或类似的东西没有默认值。 If you're trying to have a default for the struct, consider using theDefault trait .如果您尝试为结构设置默认值,请考虑使用Default trait

Second, you're probably aware of this, but there's already an implementation of a chunk iterator for slices.其次,您可能已经意识到这一点,但是已经有一个用于切片的块迭代器的实现 If slice is a slice (or can be deref coerced into a slice - vectors and arrays are prime examples), then slice.chunks(n) is an iterator over chunks of that slice with length n .如果slice是一个切片(或者可以取消引用强制转换为一个切片 - 向量和 arrays 是主要示例),那么slice.chunks(n)是一个迭代器,该迭代器对长度为n的切片的块进行迭代。 I gave an example of this in the code linked above.我在上面链接的代码中给出了一个例子。 Interestingly, that implementation uses a very similar idea: slice.chunks(n) returns a new struct with a lifetime parameter and implements Iterator .有趣的是,该实现使用了一个非常相似的想法: slice.chunks(n) 返回一个带有生命周期参数的新结构并实现Iterator This is almost exactly the same as our Data::iter .这与我们的Data::iter几乎完全相同。

Finally, your implementation of next has a bug in it that causes an out-of-bounds panic when run.最后,您的next实现中有一个错误,在运行时会导致越界恐慌。 See if you can spot it!看看你能不能看出来!

I wouldn't implement my own.我不会实现我自己的。 Instead, I'd reuse the existing chunks iterator and implement IntoIterator for a reference to the type :相反,我会重用现有的chunks迭代器并实现IntoIterator以引用类型

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>())
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM