0Pricing
Learn Rust Coding · Урок

Пользовательские итераторы

Реализация Iterator

«Пользовательские итераторы» — бесплатный урок Learn Rust Coding на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Learn Rust Coding, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Learn Rust Coding содержит 4 уроков всего.

Создаём собственный итератор

Вы не ограничены встроенными итераторами. Реализовав трейт Iterator для собственного типа, Вы бесплатно получаете все адаптеры и потребители.

Нужно предоставить только тип Item и метод next.

Структура Counter

Начните со структуры, хранящей состояние итератора. Простому счётчику достаточно текущего значения.

struct Counter {
    count: u32,
}

fn main() {
    let c = Counter { count: 0 };
    println!("start at {}", c.count);
}

Реализация next

Реализуйте Iterator для структуры. Задайте type Item, затем напишите next, чтобы изменять состояние и возвращать Some до наступления условия остановки, а затем None.

struct Counter { count: u32 }

impl Iterator for Counter {
    type Item = u32;
    fn next(&mut self) -> Option<u32> {
        if self.count < 5 {
            self.count += 1;
            Some(self.count)
        } else {
            None
        }
    }
}

fn main() {
    let mut c = Counter { count: 0 };
    println!("{:?}", c.next());
    println!("{:?}", c.next());
}

Использование в цикле for

Как только появляется next, Ваш тип работает в цикле for так же, как любой стандартный итератор.

struct Counter { count: u32 }

impl Iterator for Counter {
    type Item = u32;
    fn next(&mut self) -> Option<u32> {
        if self.count < 5 { self.count += 1; Some(self.count) } else { None }
    }
}

fn main() {
    for n in (Counter { count: 0 }) {
        print!("{} ", n);
    }
    println!();
}

Бесплатные адаптеры и потребители

Поскольку Вы реализовали один метод, теперь доступен весь набор инструментов итераторов. Здесь мы суммируем квадраты парных значений счётчика.

struct Counter { count: u32 }

impl Iterator for Counter {
    type Item = u32;
    fn next(&mut self) -> Option<u32> {
        if self.count < 5 { self.count += 1; Some(self.count) } else { None }
    }
}

fn main() {
    let total: u32 = Counter { count: 0 }.map(|x| x * 2).sum();
    println!("{}", total);
}

Метод-конструктор

Добавьте функцию new, чтобы пользователям не приходилось обращаться к внутренним полям напрямую. Это чище и позволяет оставить поле закрытым.

struct Counter { count: u32 }

impl Counter {
    fn new() -> Counter { Counter { count: 0 } }
}

impl Iterator for Counter {
    type Item = u32;
    fn next(&mut self) -> Option<u32> {
        if self.count < 3 { self.count += 1; Some(self.count) } else { None }
    }
}

fn main() {
    let v: Vec<u32> = Counter::new().collect();
    println!("{:?}", v);
}

Итератор Фибоначчи

Итераторы могут хранить более сложное состояние. Генератор Фибоначчи хранит два последних значения и обновляет их при каждом вызове next.

struct Fib { a: u64, b: u64 }

impl Iterator for Fib {
    type Item = u64;
    fn next(&mut self) -> Option<u64> {
        let current = self.a;
        self.a = self.b;
        self.b = current + self.b;
        Some(current)
    }
}

fn main() {
    let fib = Fib { a: 0, b: 1 };
    let seq: Vec<u64> = fib.take(8).collect();
    println!("{:?}", seq);
}

Бесконечные итераторы — это нормально

Итератор Фибоначчи никогда не возвращает None — он бесконечен. Это безопасно, потому что благодаря ленивости значения создаются только по запросу. Используйте take, чтобы ограничить последовательность.

struct Fib { a: u64, b: u64 }

impl Iterator for Fib {
    type Item = u64;
    fn next(&mut self) -> Option<u64> {
        let c = self.a;
        self.a = self.b;
        self.b = c + self.b;
        Some(c)
    }
}

fn main() {
    let big = Fib { a: 0, b: 1 }.nth(20);
    println!("{:?}", big);
}

Итерация по типу-обёртке

Часто реализацию Iterator добавляют для вспомогательной структуры, которая заимствует ваши данные. Здесь пошаговый итератор выдаёт каждое второе число из хранящегося внутри диапазона.

struct EvenUpTo { current: u32, max: u32 }

impl Iterator for EvenUpTo {
    type Item = u32;
    fn next(&mut self) -> Option<u32> {
        if self.current > self.max { return None; }
        let value = self.current;
        self.current += 2;
        Some(value)
    }
}

fn main() {
    let evens: Vec<u32> = (EvenUpTo { current: 0, max: 10 }).collect();
    println!("{:?}", evens);
}

Зачем создавать собственные итераторы

Собственные итераторы позволяют передавать значения потоком без выделения памяти под всю коллекцию, моделировать бесконечные или вычисляемые последовательности и предоставлять вызывающему коду знакомый, композиционный интерфейс.

Один небольшой метод next открывает доступ ко всей экосистеме.

Советы по проектированию

Храните минимум состояния, возвращайте None ровно один раз в конце (а для бесконечных итераторов — никогда) и избегайте трудоёмких операций внутри next, чтобы ленивые вычисления оставались дешёвыми.

Быстрая проверка

Реализация признака Iterator.

Итоги

Вы создали собственные итераторы:

  • Храните состояние в структуре, затем используйте impl Iterator
  • Определите type Item и напишите next
  • Сразу бесплатно получаете адаптеры и потребители
  • Итераторы могут быть конечными или бесконечными; используйте take, чтобы ограничить бесконечные

Часто задаваемые вопросы

Урок «Пользовательские итераторы» бесплатный?

Да — полный текст урока «Пользовательские итераторы» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Learn Rust Coding, подпишись на CoddyKit PRO. Курс Learn Rust Coding содержит 4 уроков всего.

Чему я научусь в уроке «Пользовательские итераторы»?

Реализация Iterator Ты практикуешь Learn Rust Coding с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Learn Rust Coding?

Предыдущий опыт не требуется. Learn Rust Coding на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Пользовательские итераторы»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Learn Rust Coding?

Да. Каждый урок Learn Rust Coding включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Трейт Iterator
  2. map, filter, collect
  3. Адаптеры и потребители
  4. Пользовательские итераторы
← Назад к Learn Rust Coding