Как реализовать итератор над кусками массива в структуре? - PullRequest
0 голосов
/ 04 ноября 2019

Я хочу реализовать итератор для структуры с массивом в качестве одного из его полей. Итератор должен вернуть часть этого массива, но для этого требуется параметр времени жизни. Куда должен идти этот параметр?

Версия 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)
    }
}

Ответы [ 2 ]

0 голосов
/ 04 ноября 2019

Я бы не реализовал свой собственный. Вместо этого я бы использовал существующий итератор 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>())
    }
}
0 голосов
/ 04 ноября 2019

Когда вы возвращаете ссылку из функции, ее время жизни должно быть привязано к чему-то другому. В противном случае компилятор не будет знать, как долго действительна ссылка (исключение составляет '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,
        }
    }
}

(детская площадка)

Просто несколько заметок. Была одна ошибка компилятора с [0u8; 100]. Возможно, это была опечатка для [u8; 100], но на всякий случай, вот почему мы не можем этого сделать. В полях для определения структуры указываются только типы. Там нет значений по умолчанию для полей или что-то подобное. Если вы пытаетесь использовать по умолчанию для структуры, рассмотрите возможность использования черты Default .

Во-вторых, вы, вероятно, знаете об этом, но уже есть реализация итератора куска для срезов. Если slice является срезом (или его можно принудительно преобразовать в срез - векторы и массивы являются простыми примерами), то slice.chunks(n) является итератором для фрагментов этого среза длиной n. Я привел пример этого в приведенном выше коде. Интересно, что эта реализация использует очень похожую идею: slice.chunks (n) возвращает новую структуру с параметром времени жизни и реализует Iterator. Это почти точно так же, как наша Data::iter.

Наконец, ваша реализация next содержит ошибку, которая вызывает панику вне пределов при запуске. Посмотри, сможешь ли ты это заметить!

...