Трейт IntoIterator
Трейт Iterator говорит, как итерироваться, когда итератор уже создан. Связанный трейт IntoIterator определяет, как создать итератор для типа. Он используется автоматически циклом for.
// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0
struct Grid {
x_coords: Vec<u32>,
y_coords: Vec<u32>,
}
impl IntoIterator for Grid {
type Item = (u32, u32);
type IntoIter = GridIter;
fn into_iter(self) -> GridIter {
GridIter { grid: self, i: 0, j: 0 }
}
}
struct GridIter {
grid: Grid,
i: usize,
j: usize,
}
impl Iterator for GridIter {
type Item = (u32, u32);
fn next(&mut self) -> Option<(u32, u32)> {
if self.i >= self.grid.x_coords.len() {
self.i = 0;
self.j += 1;
if self.j >= self.grid.y_coords.len() {
return None;
}
}
let res = Some((self.grid.x_coords[self.i], self.grid.y_coords[self.j]));
self.i += 1;
res
}
}
fn main() {
let grid = Grid { x_coords: vec![3, 5, 7, 9], y_coords: vec![10, 20, 30, 40] };
for (x, y) in grid {
println!("point = {x}, {y}");
}
// dbg!(&grid.x_coords); // ❌ Сетка была потреблена циклом.
}
IntoIterator— трейт, благодаря которому работают циклыfor. Его реализуют типы коллекций, напримерVec<T>, и ссылки на них, например&Vec<T>и&[T]. Его также реализуют диапазоны. Поэтому можно перебирать вектор черезfor i in some_vec { .. }, но методаsome_vec.next()не существует.
Перейдите по ссылке на документацию IntoIterator. Каждая реализация IntoIterator должна объявлять два типа:
Item: тип элементов, по которым идёт итерация, напримерi8,IntoIter: типIterator, который возвращает методinto_iter.
Обратите внимание, что IntoIter и Item связаны: итератор должен иметь тот же тип Item, а значит, возвращать Option<Item>.
Пример перебирает все комбинации координат x и y.
Раскомментируйте закомментированную строку в main и покажите ошибку компилятора. Обратите внимание, что IntoIterator::into_iter забирает владение self. На следующем слайде показано, как этого избежать, перебирая &Grid через дополнительную реализацию IntoIterator.