В функциональном стиле, когда тебе нужно свести последовательность значений к одному числу (произведению в данном случае), используют **свертку** (fold) или, в более простых случаях, готовые методы агрегации вроде `product`. В Rust у итераторов есть метод `product()`, который делает ровно то, что нужно для факториала. ### Решение 1: Самое идиоматичное (Iterators + product) Мы создаем диапазон от `1` до `num` (включительно) и перемножаем все числа. ```rust fn factorial(num: u64) -> u64 { (1..=num).product() } ``` **Почему это работает:** 1. `(1..=num)` создает итератор, генерирующий числа `1, 2, 3, ..., num`. * Если `num` равно `0`, диапазон `1..=0` пуст. 2. `.product()` проходит по итератору, умножая аккумулятор на каждый элемент. * Для пустого итератора (факториал 0) `product()` возвращает мультипликативную единицу (`1`), что математически верно: $0! = 1$. *** ### Решение 2: Более явное (Fold) Если бы метода `product()` не существовало, мы бы использовали `fold`. Это классическая "reduce" операция из функционального программирования. ```rust fn factorial(num: u64) -> u64 { (1..=num).fold(1, |acc, x| acc * x) } ``` **Разбор:** * `1` (первый аргумент) — начальное значение аккумулятора. * `|acc, x| acc * x` — замыкание, которое берет текущий аккумулятор (`acc`) и следующий элемент (`x`), и возвращает новое значение аккумулятора. * Для `num = 4`: 1. `acc = 1` (начальное), `x = 1` -> `1 * 1 = 1` 2. `acc = 1`, `x = 2` -> `1 * 2 = 2` 3. `acc = 2`, `x = 3` -> `2 * 3 = 6` 4. `acc = 6`, `x = 4` -> `6 * 4 = 24` **Итог:** Используй `(1..=num).product()` — это самый "ржавый" (Rustacean) и читаемый способ. Он выполняет все твои условия: * Нет явного `return` (это выражение). * Нет циклов `for/while` (спрятаны внутри итератора). * Нет мутабельных переменных. * Нет рекурсии. ### 1. "Джентльменский набор" итераторов Помимо уже знакомых тебе `map`, `filter`, `fold` и `flat_map`, вот что ты будешь использовать в 90% случаев: * **`collect()`** — ты уже знаешь, это "материализатор". Превращает поток обратно в коллекцию (`Vec`, `HashMap`, `String`...). * **`find(|x| ...)`** — ищет **первый** элемент, удовлетворяющий условию. Возвращает `Option`. * **`any(|x| ...)` / `all(|x| ...)`** — возвращают `bool`. `any` проверяет, есть ли *хотя бы один*, `all` — что *все* соответствуют. * **`enumerate()`** — добавляет индекс к элементу. Вместо `x` получаешь `(index, x)`. ```rust // Вывести строки с номерами lines.iter().enumerate().for_each(|(i, line)| println!("{}: {}", i, line)); ``` * **`zip(other_iter)`** — "сшивает" два итератора в один поток пар `(a, b)`. * **`take(n)` / `skip(n)`** — берет первые `n` или пропускает первые `n` элементов. * **`chain(other_iter)`** — склеивает два итератора: "сначала всё из первого, потом всё из второго". * **`inspect(|x| ...)`** — бесценно для отладки! Позволяет "подсмотреть" значение в середине цепочки, не меняя его (обычно там делают `println!`). ### 2. Вся ли функциональщина в Rust — это итераторы? И да, и нет. * **Да**, итераторы — это главный локомотив функционального стиля в Rust. Это самый мощный и оптимизированный инструмент для обработки данных. * **Нет**, ФП в Rust шире. Оно включает: * **Enum + Pattern Matching** (алгебраические типы данных) — это основа логики, замена классам и полиморфизму. * **Immutability** (неизменяемость по умолчанию). * **Closures** (замыкания) — функции высшего порядка, которые можно передавать и возвращать. Но именно цепочки итераторов делают код "визуально функциональным". ### 3. Каждую ли задачу можно решить функционально? **Теоретически — да.** Любой цикл можно переписать через рекурсию или `fold`. **Практически — не стоит.** Rust — прагматичный язык. Иногда **императивный код лучше**: 1. **Сложная мутация состояния**: Если тебе нужно обновлять 5 разных переменных в зависимости от сложной логики на каждом шаге, `fold` превратится в ад с кортежем из 5 элементов. Обычный `for` будет чище. 2. **Ранний выход из вложенных циклов**: Выйти из тройного вложенного цикла через `break 'label` проще, чем писать цепочку итераторов, которая умеет прерываться. 3. **Графы и произвольный доступ**: Итераторы хороши для *последовательностей*. Если ты прыгаешь по индексам массива туда-сюда (`i`, `i+5`, `i/2`), функциональный стиль будет выглядеть как натягивание совы на глобус. **Совет:** Используй итераторы для *трансформации потоков данных* (фильтрация, маппинг, поиск, агрегация). Используй циклы для *сложного управления потоком выполнения* или когда состояние слишком запутанное. В Rust нормально сочетать оба подхода.