🦀 Rust

Funktionen und Rekursion in Rust 1.75: Ausführliche Anleitung

Функции и рекурсия в Rust

Представь, что ты пишешь инструкцию для робота. Если каждый раз, когда нужно сложить два числа, писать заново всю цепочку действий — это будет долго и запутанно. Функции — это как готовые блоки инструкций, которые можно вызывать по имени. Они помогают не повторять один и тот же код, делают программу понятной и аккуратной.

Рекурсия — это когда функция вызывает саму себя. Как матрёшка: внутри большой матрёшки — такая же, но поменьше. Рекурсия помогает решать задачи, которые можно разбить на такие же, но более простые подзадачи.

Основной синтаксис функций

Функция в Rust начинается с ключевого слова fn. После имени в круглых скобках идут параметры (входные данные). После стрелки -> указывается тип возвращаемого значения. Тело функции — в фигурных скобках {}.

fn имя_функции(параметр1: тип, параметр2: тип) -> тип_результата {
    // действия
    результат // последнее выражение без точки с запятой — это возврат
}

Простой пример — функция для сложения двух чисел (как в задаче про семейный бюджет):

use std::io;

fn summa(a: i32, b: i32) -> i32 {
    a + b  // возвращаем сумму
}

fn main() {
    let mut input = String::new();
    println!("Введи два числа через пробел:");
    io::stdin().read_line(&mut input).expect("Ошибка чтения");
    
    let parts: Vec<&str> = input.trim().split_whitespace().collect();
    let x: i32 = parts[0].parse().unwrap();
    let y: i32 = parts[1].parse().unwrap();
    
    let result = summa(x, y);
    println!("Сумма доходов: {}", result);
}

Сравнение и выбор (задача про температуры)

Функция может принимать два значения и возвращать одно из них. Например, найдём бóльшую температуру:

use std::io;

fn max_temperature(t1: f64, t2: f64) -> f64 {
    if t1 > t2 {
        t1
    } else {
        t2
    }
}

fn main() {
    let mut input = String::new();
    println!("Введи две температуры через пробел (например, 23.5 18.2):");
    io::stdin().read_line(&mut input).expect("Ошибка");
    
    let parts: Vec<&str> = input.trim().split_whitespace().collect();
    let temp1: f64 = parts[0].parse().unwrap();
    let temp2: f64 = parts[1].parse().unwrap();
    
    let higher = max_temperature(temp1, temp2);
    println!("Более высокая температура: {}", higher);
}

Проверка чётности

Функция может возвращать не только числа, но и логическое значение bool (true/false). Проверим, чётное ли число коробок:

use std::io;

fn is_even(n: i32) -> bool {
    n % 2 == 0  // если остаток от деления на 2 равен 0 — число чётное
}

fn main() {
    let mut input = String::new();
    println!("Сколько коробок произведено?");
    io::stdin().read_line(&mut input).expect("Ошибка");
    let boxes: i32 = input.trim().parse().unwrap();
    
    if is_even(boxes) {
        println!("Число коробок чётное, можно упаковывать в пары.");
    } else {
        println!("Число коробок нечётное, одна коробка останется без пары.");
    }
}

Рекурсия: реверс строки

Рекурсивная функция — это функция, которая вызывает саму себя. Разберём задачу: вывести строку задом наперёд. Идея: берём первый символ, а для оставшейся части строки снова вызываем ту же функцию.

use std::io;

fn reverse_string(s: &str) -> String {
    if s.len() <= 1 {
        // если в строке 0 или 1 символ — возвращаем как есть
        s.to_string()
    } else {
        // берём последний символ + рекурсивно обрабатываем остаток
        let last = &s[s.len()-1..];
        let rest = &s[..s.len()-1];
        format!("{}{}", last, reverse_string(rest))
    }
}

fn main() {
    let mut input = String::new();
    println!("Введи строку для реверса:");
    io::stdin().read_line(&mut input).expect("Ошибка");
    let text = input.trim();
    
    let reversed = reverse_string(text);
    println!("Перевёрнутая строка: {}", reversed);
}

Рекурсия: поиск минимума в списке

Найдём самую низкую температуру за день. Список будем передавать в функцию как срез (&[i32]). Рекурсия: если в списке один элемент — он и есть минимум. Иначе сравниваем первый элемент с минимумом из остатка списка.

use std::io;

fn find_min(arr: &[i32]) -> i32 {
    if arr.len() == 1 {
        arr[0]
    } else {
        let min_of_rest = find_min(&arr[1..]);
        if arr[0] < min_of_rest {
            arr[0]
        } else {
            min_of_rest
        }
    }
}

fn main() {
    println!("Введи температуры через пробел (например: 15 12 18 10 14):");
    let mut input = String::new();
    io::stdin().read_line(&mut input).expect("Ошибка");
    
    let numbers: Vec<i32> = input
        .trim()
        .split_whitespace()
        .map(|s| s.parse().unwrap())
        .collect();
    
    let min_temp = find_min(&numbers);
    println!("Самая низкая температура за день: {}", min_temp);
}

Частые ошибки новичков

  • Забыть return или точку с запятой: В Rust последнее выражение в функции автоматически возвращается, но если поставить точку с запятой — оно станет инструкцией и ничего не вернёт. Будет ошибка компиляции.
  • Бесконечная рекурсия: Если не написать условие остановки (базовый случай), функция будет вызывать себя бесконечно, пока не закончится память. Всегда проверяй, когда рекурсия должна остановиться.
  • Путаница с типами: В Rust строгая типизация. Нельзя просто так сложить f64 и i32 — нужно явно преобразовывать типы.
  • Забыть про & для строк: В Rust строка — сложный тип. Функции часто принимают &str (ссылку на строку), а не саму строку String.

Итоговый пример: "Калькулятор скидки в магазине"

Представь, что в магазине действует акция: если сумма покупки больше 1000 рублей, дают скидку 10%. Напишем функцию, которая считает итоговую цену, а затем рекурсивную функцию, которая применяет скидку несколько раз (например, если товар участвует в двух акциях).

use std::io;

// Обычная функция: применяет скидку один раз
fn apply_discount(price: f64) -> f64 {
    if price > 1000.0 {
        price * 0.9  // скидка 10%
    } else {
        price
    }
}

// Рекурсивная функция: применяет скидку n раз
fn apply_discount_n_times(price: f64, times: i32) -> f64 {
    if times == 0 {
        price  // базовый случай: скидок больше нет
    } else {
        let discounted = apply_discount(price);
        apply_discount_n_times(discounted, times - 1)
    }
}

fn main() {
    let mut input = String::new();
    println!("Введи сумму покупки:");
    io::stdin().read_line(&mut input).expect("Ошибка");
    let amount: f64 = input.trim().parse().unwrap();
    
    println!("Введи количество применений скидки:");
    input.clear();
    io::stdin().read_line(&mut input).expect("Ошибка");
    let n: i32 = input.trim().parse().unwrap();
    
    let final_price = apply_discount_n_times(amount, n);
    println!("Итоговая цена после {} применений скидки: {:.2}", n, final_price);
}

Теперь ты знаешь, как с помощью функций и рекурсии решать самые разные задачи — от подсчёта бюджета до обработки списков. Главное — разбивай задачу на маленькие шаги и не бойся, что функция вызывает саму себя. Это как лестница: чтобы спуститься с верхнего этажа, нужно сначала сделать шаг на предыдущий!