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

时间:2019-11-03 22:22:41

标签: rust iterator lifetime

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

Rust版本为1.37.0

where('occupied', 1)

2 个答案:

答案 0 :(得分:0)

从函数返回引用时,其生存期需要与其他事物联系在一起。否则,编译器将不知道引用有效的时间(此例外是'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的实现中存在一个错误,该错误在运行时会导致越界恐慌。看看是否能找到它!

答案 1 :(得分:0)

我不会实现自己的。相反,我将重用现有的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>())
    }
}