Flow Control
IF-ELSE
В Rust есть управление потоком программы через конструкции IF, ELSE IF, ELSE:
let test_number = 6;
if test_number % 4 == 0 {
println!("Divisible by 4");
} else if test_number % 3 == 0 { // Проверка останавливается на первом
println!("Divisible by 3"); // выполнимом условии, дальнейшие проверки
} else if test_number % 2 == 0 { // пропускаются.
println!("Divisible by 2");
} else {
println!("Number is not divisible by 4, 3 or 2");
}Конструкция IF является выражением (expression) и возвращает значение:
let condition = true;
let number = if condition { "aa" } else { "ab" }; // присваивание результата IF
println!("Number is {number}");Используйте if как выражение для компактности. Для сложных случаев лучше переходить к match.
LOOPS
Три варианта организовать цикл: через операторы loop, while, for.
Loop
loop - организация вечных циклов. Конструкция loop является выражением (expression), поэтому возвращает значение.
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // выход из вечного цикла
}
}; // ";" нужно, т.к. было выражение
println!("The result is {result}");Если делать вложенные циклы, то можно помечать их меткой, чтобы выходить с break на нужный уровень.
let mut count = 0;
'counting_up: loop { // метка внешнего цикла
println!("count = {count}");
let mut remaining = 10;
loop {
println!("remaining = {remaining}");
if remaining == 9 {
break;
}
if count == 2 {
break 'counting_up; // goto метка
}
remaining -= 1;
}
count += 1;
}
println!("End count = {count}");While
while - цикл с условием. Можно реализовать через loop, но с while сильно короче.
let mut number = 10;
while number != 0 {
println!("{number}!");
number -= 1;
}
println!("ЗАПУСК!");For
for - цикл по множествам элементов. В том числе можно задать подмножество чисел.
for i in (1..10).rev() { // .rev() - выдача подмножества в обратную сторону
println!("Value: {i}");
}
println!("ЗАПУСК!");Функции
Вызов функции: указатель на начало функции кладётся на стек (под каждую функцию выделяется место на стеке - stack frame). Стек имеет ограничения по размеру. Можно это проверить, написав пример:
fn main() {
a();
}
fn a() {
println!("Calling B!");
b();
}
fn b() {
println!("Calling C!");
c();
}
fn c() {
println!("Calling A!");
a(); // бесконечный цикл вызовов
}При запуске программы она бесконечно вызывает функции, пока не исчерпает место в stack frame функции main, и тогда программа падает с ошибкой:
thread 'main' has overflowed its stack
fatal runtime error: stack overflow
Abort trap: 6Передача функций как аргументов
Тип функции — её сигнатура.
// Функция apply принимает 2 параметра:
// 1. x типа i32 (32-битное целое число)
// 2. f - функцию с сигнатурой fn(i32) -> i32,
// т.е. которая принимает i32 и возвращает i32
// Сама apply возвращает i32
fn apply(x: i32, f: fn(i32) -> i32) -> i32 {
// Применяем переданную функцию f к аргументу x
// и возвращаем результат
f(x)
}
// Функция принимает x типа i32 и возвращает x * 2 тоже типа i32
fn double(x: i32) -> i32 {
x * 2 // удваивает входное число
}
fn main() {
// Вызываем apply с двумя аргументами:
// 1. Число 5
// 2. Функция double
// double будет применена к 5, то есть 5 * 2 = 10
let result = apply(5, double);
println!("Удвоенное: {}", result); // Вывод: Удвоенное: 10
}Exit
В Rust для “нормального” завершения программы используется функция std::process::exit из стандартной библиотеки. Она завершает программу немедленно с заданным кодом возврата, не вызывая панику.
use std::process;
fn main() {
println!("До выхода");
process::exit(0); // Завершаем программу с кодом 0 (успех)
println!("Это не выведется");
}process::exit(code: i32)принимает код возврата (i32), который передаётся операционной системе.- Код
0обычно означает “успешное завершение”, а ненулевые значения (напримр,1) — “ошибка”. - После вызова
exitпрограмма завершается мгновенно — никакой код ниже не выполняется, даже если есть незавершённые операции.
Return
Можно использовать return в main. Это не то же самое, что exit (не мгновенно прерывает), но подходит для естественного завершения:
fn main() {
println!("Работаем...");
return; // Завершаем с кодом 0
println!("Это не выведется");
}Result
Можно возвращать Result из main, чтобы указать успех или ошибку:
fn main() -> Result<(), i32> {
println!("Работаем...");
Ok(()); // Успех, код 0
// или Err(1) для ошибки с кодом 1
}- Если нужно мгновенное завершение с кодом возврата — используй
std::process::exit; - Если надо завершить программу “правильно” в конце
main— простоreturnилиResult; panic!- для случаев, когда программа должна “упасть” из-за ошибки.