Замыкания и итераторы

Замыкания — анонимные функции, захватывающие окружение; итераторы лениво обрабатывают последовательности.

Замыкание (closure) — анонимная функция, которая может захватывать переменные из окружающего кода.

Замыкания

Замыкание — это функция без имени, которую можно сохранить в переменную или передать другой функции. Параметры записывают между вертикальными чертами |...|.

fn main() {
    let add = |a: i32, b: i32| a + b;   // замыкание из двух аргументов
    let square = |x| x * x;             // типы выводятся
    println!("{}", add(2, 3));
    println!("{}", square(5));
}

Вывод:

5
25

Главная особенность — замыкание захватывает переменные из окружения. Обычная функция так не умеет.

fn main() {
    let factor = 10;
    let multiply = |x| x * factor; // factor захвачен из окружения
    println!("{}", multiply(5));
}

Вывод:

50

Итераторы

Итератор — это последовательность значений, которую можно обходить. Вызов .iter() на коллекции даёт итератор. Его сила — в цепочках преобразований map, filter и других, которые принимают замыкания.

fn main() {
    let nums = vec![1, 2, 3, 4, 5, 6];

    // оставить чётные, удвоить, собрать в новый вектор
    let result: Vec<i32> = nums
        .iter()
        .filter(|&&x| x % 2 == 0) // оставляем чётные
        .map(|&x| x * 2)          // удваиваем
        .collect();               // собираем в Vec

    println!("{:?}", result);
}

Вывод:

[4, 8, 12]

Ленивость итераторов

Важная деталь: map и filter ленивы — сами по себе они ничего не вычисляют, а только описывают, что нужно сделать. Работа происходит только когда цепочку «завершают» методом-потребителем вроде collect, sum или for. Благодаря этому промежуточных коллекций не создаётся, и цепочки остаются быстрыми.

fn main() {
    let nums = vec![1, 2, 3, 4, 5];
    // сумма квадратов нечётных чисел
    let total: i32 = nums
        .iter()
        .filter(|&&x| x % 2 == 1)
        .map(|&x| x * x)
        .sum(); // потребитель: запускает всю цепочку
    println!("{total}");
}

Вывод:

35

(1 + 9 + 25 = 35: квадраты 1, 3 и 5.) Такие цепочки заменяют циклы с ручным накоплением и читаются как описание задачи, а не как пошаговая инструкция.

Часто используемые методы итераторов

МетодЧто делает
map(f)преобразовать каждый элемент
filter(p)оставить элементы, удовлетворяющие условию
collect()собрать в коллекцию (Vec, HashMap...)
sum() / count()сумма / количество элементов
enumerate()добавить индекс к каждому элементу

Итог

  • Замыкания — анонимные функции |args| тело, способные захватывать окружение.
  • Итераторы с map/filter/collect выражают обработку данных декларативно.
  • Итераторы ленивы: цепочка вычисляется только при вызове метода-потребителя (collect, sum).
Проверьте себя
1. Чем замыкание отличается от обычной функции?
AНичем
BЗамыкание анонимно и может захватывать переменные из окружающего кода
CЗамыкание быстрее
DЗамыкание не принимает аргументов
2. Когда выполняется цепочка iter().filter().map()?
AСразу при вызове filter
BЛениво — только когда вызывают потребитель вроде collect, sum или for
CНикогда
DПри объявлении вектора
3. Что делает метод collect()?
AУдаляет элементы
BСобирает результат итератора в коллекцию, например Vec
CСортирует элементы
DПечатает элементы
Поддержать проект