Flujo de control en Rust
Условия и циклы в Rust: if, match, loop, for с примерами
Представь светофор: зелёный — едешь, красный — ждёшь. Программа тоже принимает решения в зависимости от условий. В Rust для этого есть несколько конструкций — и некоторые удивительно умные!
if — условное выражение (не просто оператор!)
В Rust if — это выражение: оно возвращает значение! В большинстве языков if — просто команда. В Rust это ещё и значение.
fn main() {
let number = 7;
if number < 5 {
println!("Маленькое");
} else if number < 10 {
println!("Среднее"); // выведется это
} else {
println!("Большое");
}
// ВАЖНО: условие ОБЯЗАТЕЛЬНО bool в Rust!
// if number { ... } — ОШИБКА! (не как Python/JS)
if number != 0 {
println!("Не ноль!");
}
}
if как выражение
fn main() {
let age = 16;
let status = if age >= 18 { "совершеннолетний" } else { "несовершеннолетний" };
println!("Статус: {}", status);
let score = 75u32;
let grade = if score >= 90 { 5 }
else if score >= 75 { 4 }
else if score >= 60 { 3 }
else { 2 };
println!("Оценка: {}", grade); // 4
let max = if 3 > 5 { 3 } else { 5 };
println!("Максимум: {}", max); // 5
// Обе ветки ОБЯЗАНЫ возвращать одинаковый тип:
// let x = if true { 5 } else { "пять" }; — ОШИБКА!
}
loop — бесконечный цикл с возвратом значения
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // выходим и возвращаем значение!
}
};
println!("counter={}, result={}", counter, result); // 10, 20
// Метки для вложенных циклов
'outer: loop {
let mut y = 0;
loop {
y += 1;
if y == 3 { continue 'outer; }
if y > 5 { break 'outer; }
}
}
println!("Готово!");
}
while — цикл с условием
fn main() {
let mut n = 5;
while n > 0 {
println!("{}...", n);
n -= 1;
}
println!("🚀 ПУСК!");
let mut sum = 0;
let mut i = 0;
while sum < 100 {
i += 1;
sum += i;
}
println!("1+2+...+{} = {} (первая сумма >= 100)", i, sum);
}
for — цикл по коллекции
fn main() {
let fruits = ["яблоко", "банан", "вишня", "дыня"];
// Простой обход
for fruit in fruits {
println!("Фрукт: {}", fruit);
}
// С индексом
for (i, fruit) in fruits.iter().enumerate() {
println!("{}: {}", i + 1, fruit);
}
// По вектору — & чтобы не перемещать данные
let numbers = vec![10, 20, 30, 40, 50];
let mut total = 0;
for n in &numbers {
total += n;
}
println!("Сумма: {}", total);
println!("Вектор жив: {:?}", numbers);
// Изменяем через &mut
let mut scores = vec![85, 90, 78];
for s in &mut scores {
*s += 5; // * — разыменование ссылки
}
println!("С бонусом: {:?}", scores);
}
Диапазоны (Range)
fn main() {
// 1..10 — от 1 до 9 (НЕ включая 10)
for i in 1..10 { print!("{} ", i); }
println!();
// 1..=10 — включая 10
for i in 1..=10 { print!("{} ", i); }
println!();
// Обратный порядок
for i in (1..=5).rev() { print!("{} ", i); }
println!();
// Шаг step_by
for i in (0..=20).step_by(2) { print!("{} ", i); }
println!();
// Задача Гаусса
let sum: i32 = (1..=100).sum();
println!("1+2+...+100 = {}", sum); // 5050
// 10 факториал
let fact: u64 = (1..=10).product();
println!("10! = {}", fact); // 3628800
// Квадраты через map
let sq: Vec = (1..=5).map(|n| n * n).collect();
println!("{:?}", sq); // [1, 4, 9, 16, 25]
// Алфавит
let abc: String = (b'a'..=b'z').map(|c| c as char).collect();
println!("{}", abc);
}
match — замена switch, но умнее!
Компилятор требует обработать все возможные случаи. Забыл — не скомпилируется!
fn main() {
let day = 3u32;
let name = match day {
1 => "Понедельник",
2 => "Вторник",
3 => "Среда",
4 => "Четверг",
5 => "Пятница",
6 => "Суббота",
7 => "Воскресенье",
_ => "Неверный день", // _ = всё остальное
};
println!("{}", name);
// Несколько значений через |
let is_weekend = match day { 6 | 7 => true, _ => false };
println!("Выходной: {}", is_weekend);
// Диапазон в match
let score = 85u32;
let grade = match score {
90..=100 => "Отлично",
75..=89 => "Хорошо",
60..=74 => "Удовлетворительно",
0..=59 => "Неудовлетворительно",
_ => "Неверно",
};
println!("Оценка: {}", grade);
// Захват значения через @
let n = 13u32;
let desc = match n {
num @ 1..=9 => format!("Однозначное: {}", num),
num @ 10..=99 => format!("Двузначное: {}", num),
num => format!("Большое: {}", num),
};
println!("{}", desc);
// Guard (дополнительное условие)
let pair = (2, -2i32);
match pair {
(x, y) if x + y == 0 => println!("{} + {} = 0", x, y),
(x, _) if x % 2 == 0 => println!("Первое чётное: {}", x),
_ => println!("Другое"),
}
}
match с enum
enum Shape {
Circle(f64),
Rectangle(f64, f64),
Triangle(f64, f64, f64),
}
fn area(s: &Shape) -> f64 {
match s {
Shape::Circle(r) => std::f64::consts::PI * r * r,
Shape::Rectangle(w, h) => w * h,
Shape::Triangle(a, b, c) => {
let s = (a + b + c) / 2.0;
(s * (s-a) * (s-b) * (s-c)).sqrt()
}
}
}
fn main() {
let shapes = vec![
Shape::Circle(5.0),
Shape::Rectangle(4.0, 6.0),
Shape::Triangle(3.0, 4.0, 5.0),
];
for shape in &shapes {
println!("Площадь: {:.2}", area(shape));
}
}
Практический пример: игра «Угадай число»
fn main() {
let secret = 42u32;
let guesses = [25, 60, 35, 45, 42];
println!("🎮 Угадай число от 1 до 100!");
let mut won = false;
for (n, &guess) in guesses.iter().enumerate() {
let hint = match guess.cmp(&secret) {
std::cmp::Ordering::Less => format!("⬆️ {} — меньше", guess),
std::cmp::Ordering::Greater => format!("⬇️ {} — больше", guess),
std::cmp::Ordering::Equal => format!("🎉 {} — УГАДАЛ!", guess),
};
println!("Попытка {}: {}", n + 1, hint);
if guess == secret { won = true; break; }
}
println!("{}", if won { "Победа!" } else { "Не угадал" });
}
Типичные ошибки
❌ Условие не bool
if 5 { ... } // error: expected `bool`, found integer
if 5 != 0 { ... } // ✅
❌ Неполный match
match x {
1 => ...,
2 => ...,
// error: non-exhaustive patterns
_ => ..., // добавь wildcard
}
Итого
if/else if/else — условие и выражение с возвращаемым значением. loop — бесконечный цикл; break значение возвращает результат. while условие. for x in коллекция. 1..10 — диапазон без 10; 1..=10 — включая. match — проверяет все случаи. Метки 'outer: для вложенных циклов.
Дополнительные примеры для практики
Программирование — это навык, который улучшается только с практикой. Вот несколько задач для самостоятельной работы:
Задача 1: Числовые паттерны
fn main() {
// Выведи пирамиду из звёздочек
for i in 1..=5 {
for _ in 0..i { print!("* "); }
println!();
}
// Числа Фибоначчи итеративно (быстрее рекурсии)
let mut a = 0u64;
let mut b = 1u64;
print!("Фибоначчи: ");
for _ in 0..15 {
print!("{} ", a);
let c = a + b;
a = b;
b = c;
}
println!();
// Простые числа через решето Эратосфена
let n = 50usize;
let mut is_prime = vec![true; n + 1];
is_prime[0] = false;
is_prime[1] = false;
let mut i = 2;
while i * i <= n {
if is_prime[i] {
let mut j = i * i;
while j <= n {
is_prime[j] = false;
j += i;
}
}
i += 1;
}
print!("Простые до {}: ", n);
for k in 2..=n {
if is_prime[k] { print!("{} ", k); }
}
println!();
}
Задача 2: Работа с данными
fn main() {
let data = vec![
("Алиса", 85.0f64),
("Боря", 72.0),
("Вера", 91.0),
("Гриша", 68.0),
("Даша", 95.0),
];
// Сортировка по убыванию
let mut sorted = data.clone();
sorted.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
println!("Топ учеников:");
for (i, (name, score)) in sorted.iter().enumerate() {
let medal = match i { 0 => "🥇", 1 => "🥈", 2 => "🥉", _ => " " };
println!("{} {} — {:.1}", medal, name, score);
}
let avg = data.iter().map(|(_, s)| s).sum::() / data.len() as f64;
let above: Vec<_> = data.iter().filter(|(_, s)| *s > avg).collect();
println!("
Средний балл: {:.2}", avg);
println!("Выше среднего: {}", above.len());
}
Разбор типичных задач
// Палиндром
fn is_palindrome(s: &str) -> bool {
let chars: Vec = s.chars().collect();
let n = chars.len();
for i in 0..n/2 {
if chars[i] != chars[n-1-i] { return false; }
}
true
}
// FizzBuzz
fn fizzbuzz(n: u32) -> String {
match (n % 3, n % 5) {
(0, 0) => "FizzBuzz".to_string(),
(0, _) => "Fizz".to_string(),
(_, 0) => "Buzz".to_string(),
_ => n.to_string(),
}
}
// Подсчёт слов в тексте
fn word_frequencies(text: &str) -> std::collections::HashMap<&str, usize> {
let mut freq = std::collections::HashMap::new();
for word in text.split_whitespace() {
*freq.entry(word).or_insert(0) += 1;
}
freq
}
fn main() {
println!("'racecar' палиндром? {}", is_palindrome("racecar"));
println!("'hello' палиндром? {}", is_palindrome("hello"));
for n in 1..=20 { print!("{} ", fizzbuzz(n)); }
println!();
let text = "rust rust код код код программирование";
let freq = word_frequencies(text);
let mut pairs: Vec<_> = freq.iter().collect();
pairs.sort_by(|a, b| b.1.cmp(a.1));
for (word, count) in pairs {
println!("'{}': {}", word, count);
}
}
Дополнительные примеры и упражнения
Работа с коллекциями
use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::VecDeque;
fn main() {
// HashMap — ассоциативный массив
let mut scores: HashMap> = HashMap::new();
scores.entry("Алиса".to_string()).or_insert_with(Vec::new).push(5);
scores.entry("Алиса".to_string()).or_insert_with(Vec::new).push(4);
scores.entry("Боря".to_string()).or_insert_with(Vec::new).push(3);
scores.entry("Боря".to_string()).or_insert_with(Vec::new).push(4);
for (name, grades) in &scores {
let avg: f64 = grades.iter().sum::() as f64 / grades.len() as f64;
println!("{}: avg={:.2} grades={:?}", name, avg, grades);
}
// HashSet — уникальные значения
let mut seen: HashSet = HashSet::new();
let data = vec![1, 2, 2, 3, 3, 3, 4, 4, 4, 4];
let mut unique = Vec::new();
for &n in &data {
if seen.insert(n) {
unique.push(n);
}
}
println!("Уникальные: {:?}", unique);
// Пересечение и объединение множеств
let set_a: HashSet<_> = [1, 2, 3, 4, 5].iter().collect();
let set_b: HashSet<_> = [3, 4, 5, 6, 7].iter().collect();
let intersection: Vec<_> = set_a.intersection(&set_b).collect();
let union: Vec<_> = set_a.union(&set_b).collect();
println!("Пересечение: {:?}", intersection);
println!("Объединение: {:?}", union);
// VecDeque — двусторонняя очередь
let mut queue: VecDeque = VecDeque::new();
queue.push_back(1);
queue.push_back(2);
queue.push_back(3);
queue.push_front(0);
println!("Очередь: {:?}", queue);
println!("Извлекли спереди: {:?}", queue.pop_front());
}
Обработка ошибок и ? оператор
use std::num::ParseIntError;
use std::fmt;
#[derive(Debug)]
enum AppError {
ParseError(ParseIntError),
ValidationError(String),
NotFound(String),
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AppError::ParseError(e) => write!(f, "Ошибка парсинга: {}", e),
AppError::ValidationError(msg) => write!(f, "Ошибка валидации: {}", msg),
AppError::NotFound(key) => write!(f, "Не найдено: {}", key),
}
}
}
impl From for AppError {
fn from(e: ParseIntError) -> Self { AppError::ParseError(e) }
}
fn parse_positive(s: &str) -> Result {
let n: i32 = s.trim().parse()?; // ? преобразует ParseIntError через From
if n <= 0 {
return Err(AppError::ValidationError(format!("{} не положительное", n)));
}
Ok(n as u32)
}
fn find_square_root(n: &str) -> Result {
let num = parse_positive(n)?;
Ok((num as f64).sqrt())
}
fn main() {
let inputs = ["25", "-5", "abc", "100", "0"];
for input in inputs {
match find_square_root(input) {
Ok(r) => println!("√{} = {:.4}", input, r),
Err(e) => println!("Ошибка для '{}': {}", input, e),
}
}
}
Итераторы — ленивые вычисления
fn main() {
let numbers = vec![1i32, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// Цепочка итераторов — ленивая, выполняется при collect/sum/etc
let result: Vec = numbers.iter()
.filter(|&&n| n % 2 == 0) // только чётные: 2,4,6,8,10
.map(|&n| n * n) // квадраты: 4,16,36,64,100
.filter(|&n| n > 20) // > 20: 36,64,100
.map(|n| format!("{}²", (n as f64).sqrt() as i32)) // "6²", "8²", "10²"
.collect();
println!("{:?}", result);
// zip — объединяем два итератора
let names = vec!["Алиса", "Боря", "Вера"];
let grades = vec![4.8, 3.9, 4.5];
let combined: Vec<_> = names.iter().zip(grades.iter()).collect();
for (name, grade) in &combined {
println!("{}: {:.1}", name, grade);
}
// enumerate + zip
let numbered: Vec<_> = names.iter()
.zip(grades.iter())
.enumerate()
.map(|(i, (n, g))| format!("{}. {}: {}", i+1, n, g))
.collect();
println!("{:?}", numbered);
// scan — промежуточные результаты (как fold, но возвращает итератор)
let running_sum: Vec = (1..=5)
.scan(0, |acc, x| { *acc += x; Some(*acc) })
.collect();
println!("Нарастающая сумма: {:?}", running_sum); // [1, 3, 6, 10, 15]
// flat_map — flatten + map
let words = vec!["hello world", "foo bar baz"];
let all_words: Vec<&str> = words.iter()
.flat_map(|s| s.split_whitespace())
.collect();
println!("{:?}", all_words);
// take и skip
let first_5: Vec = (1..).take(5).collect();
let skip_5: Vec = (1..=10).skip(5).collect();
println!("take(5): {:?}", first_5);
println!("skip(5): {:?}", skip_5);
}
Полный проект: студенческая база данных
use std::collections::HashMap;
#[derive(Debug, Clone)]
struct Student {
name: String,
class: String,
grades: HashMap,
}
impl Student {
fn new(name: &str, class: &str) -> Self {
Student {
name: name.to_string(),
class: class.to_string(),
grades: HashMap::new(),
}
}
fn add_grade(&mut self, subject: &str, grade: f64) {
self.grades.insert(subject.to_string(), grade);
}
fn gpa(&self) -> f64 {
if self.grades.is_empty() { return 0.0; }
self.grades.values().sum::() / self.grades.len() as f64
}
fn is_excellent(&self) -> bool {
self.gpa() >= 4.5 && self.grades.values().all(|&g| g >= 4.0)
}
}
struct Database {
students: Vec,
}
impl Database {
fn new() -> Self { Database { students: Vec::new() } }
fn add(&mut self, student: Student) { self.students.push(student); }
fn by_class(&self, class: &str) -> Vec<&Student> {
self.students.iter().filter(|s| s.class == class).collect()
}
fn top_students(&self, n: usize) -> Vec<&Student> {
let mut sorted: Vec<&Student> = self.students.iter().collect();
sorted.sort_by(|a, b| b.gpa().partial_cmp(&a.gpa()).unwrap());
sorted.into_iter().take(n).collect()
}
fn class_average(&self, class: &str) -> f64 {
let class_students: Vec<_> = self.by_class(class);
if class_students.is_empty() { return 0.0; }
class_students.iter().map(|s| s.gpa()).sum::() / class_students.len() as f64
}
}
fn main() {
let mut db = Database::new();
let mut alice = Student::new("Алиса", "6А");
alice.add_grade("Математика", 5.0);
alice.add_grade("Русский", 5.0);
alice.add_grade("Английский", 4.0);
let mut bob = Student::new("Боря", "6Б");
bob.add_grade("Математика", 4.0);
bob.add_grade("Русский", 3.0);
bob.add_grade("Английский", 4.0);
let mut vera = Student::new("Вера", "6А");
vera.add_grade("Математика", 5.0);
vera.add_grade("Русский", 5.0);
vera.add_grade("Английский", 5.0);
db.add(alice);
db.add(bob);
db.add(vera);
println!("=== Класс 6А ===");
for s in db.by_class("6А") {
println!("{}: GPA={:.2} Отличник?{}", s.name, s.gpa(), s.is_excellent());
}
println!("\n=== Топ 2 студента ===");
for s in db.top_students(2) {
println!("{} ({}): {:.2}", s.name, s.class, s.gpa());
}
println!("\nСредний 6А: {:.2}", db.class_average("6А"));
println!("Средний 6Б: {:.2}", db.class_average("6Б"));
}
Советы для дальнейшего обучения
- The Rust Book — официальная книга, доступна на doc.rust-lang.org/book
- Rustlings — интерактивные упражнения:
cargo install rustlings - Rust by Example — примеры кода: doc.rust-lang.org/rust-by-example
- Exercism.io — задачи с ментором по треку Rust
- Crates.io — репозиторий пакетов для Rust