Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

IntoIterator для ссылок

Если реализовать IntoIterator напрямую для типа, создание итератора забирает владение объектом, поэтому распространённый паттерн — реализовать его для ссылки, чтобы можно было создавать заимствующие итераторы.

// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0

struct Grid {
    x_coords: Vec<u32>,
    y_coords: Vec<u32>,
}

impl<'a> IntoIterator for &'a Grid {
    type Item = (&'a u32, &'a u32);
    type IntoIter = GridRefIter<'a>;
    fn into_iter(self) -> GridRefIter<'a> {
        GridRefIter { grid: self, i: 0, j: 0 }
    }
}

struct GridRefIter<'a> {
    grid: &'a Grid,
    i: usize,
    j: usize,
}

impl<'a> Iterator for GridRefIter<'a> {
    type Item = (&'a u32, &'a u32);

    fn next(&mut self) -> Option<Self::Item> {
        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 для &Grid, мы можем передать ссылку в цикл for и получить заимствующий итератор. Это позволяет заимствовать объект сетки в цикле, не забирая владение им.

  • Этот playground показывает, что GridIter и GridRefIter можно использовать одновременно.

  • Этот паттерн широко используется в стандартной библиотеке для её типов коллекций: он позволяет неявно создавать владеющие и заимствующие итераторы в зависимости от того, передаётся ли объект по значению, по ссылке или по изменяемой ссылке.

  • Откройте документацию IntoIterator и покажите реализации для [T; N], &[T; N] и &mut [T; N]. Покажите, что они создают разные итераторы с разными типами элементов:

    • Итератор для [T; N] выдаёт T по значению.
    • Итератор для &[T; N] выдаёт &T.
    • Итератор для &mut [T; N] выдаёт &mut T.