Rust: с нуля до своего органайзера Урок 14 из 20
Функции
Урок 14. Отделить вычисление от вывода и объяснить передачу значений.
Текст переведён с помощью ИИ.
Предварительно: уроки 1–13. Если непонятны переменные, вернитесь к уроку 9.
Зачем это нужно
Мы уже пишем fn main(). Теперь дадим имя собственному вычислению. Функция — именованный участок кода, который можно вызвать с входными значениями и получить результат. Как рецепт одной операции: его описывают один раз и используют с разными порциями. Но функция работает только с переданными и доступными ей данными, а не «понимает» весь проект.
Разделим две задачи: посчитать остаток и напечатать его. Это поможет проверять вычисления отдельно от сообщений. Условия, блоки и их значения берём из уроков 11–12.
Запуск примера
Работаем в проекте organizer, созданном в уроке 6, в папке с Cargo.toml. Сохраните свою предыдущую работу отдельно. Замените весь src/main.rs первым примером ниже, сохраните файл и выполните cargo run в терминале этой папки. Как объяснялось в уроке 6, команда собирает и запускает программу. Остальные файлы менять не нужно; зависимости не добавляем. Каждый следующий полный пример также заменяет файл целиком. В блоке результата показан только вывод программы, без сообщений Cargo. Сначала предскажите его, потом запускайте.
До примера разберём подпись функции. В fn remaining(total: u32, done: u32) -> u32 имена и типы внутри скобок — параметры, места для входных значений; запятая их разделяет. -> u32 обещает вернуть число этого типа. В вызове remaining(total, done) конкретные передаваемые значения — аргументы; они попадают в параметры по порядку. Последнее выражение без ; становится результатом, как в блоках урока 12. Функция без -> возвращает уже знакомое ().
fn remaining(total: u32, done: u32) -> u32 {
total - done
}
fn print_remaining(count: u32) {
println!("Осталось: {count}");
}
fn main() {
let total: u32 = 7;
let done: u32 = 2;
if done <= total {
let count = remaining(total, done);
print_remaining(count);
} else {
println!("Ошибка данных");
}
}
Осталось: 5
Разбор
fn объявляет функцию. remaining — выбранное нами имя, «остаток». В скобках записаны параметры — имена входных значений с обязательными типами. Запятая разделяет параметры. -> u32 указывает тип возвращаемого результата. В фигурных скобках — тело функции. Последняя строка total - done без ; даёт результат, как значение блока в уроке 12.
Аргументы — конкретные значения при вызове. В remaining(total, done) передаются значения переменных вызывающей функции. Первый аргумент попадает первому параметру, второй — второму: одинаковые имена не означают связь по имени. remaining(7, 2) тоже допустимый вызов. Функция remaining не видит локальные переменные main; её total и done — собственные имена параметров. При каждом вызове параметры получают значения заново.
Объявление функции не выполняет её тело. Выполнение начинается с main, достигает вызова, выполняет вызванную функцию и возвращается к следующему действию. В этом файле объявления можно поставить до или после main: порядок объявления не задаёт порядок вызовов. Повторные вызовы не сохраняют локальный счётчик сами по себе.
print_remaining(count) только печатает. Когда -> ... отсутствует, тип результата — () из урока 12. Вывод на экран — действие, а не возвращённое число. Обычный вызов функции пишется без !; println! остаётся макросом.
Договор о входных данных
Наша remaining требует done <= total. Это предусловие: правило, которое должен обеспечить вызывающий код. Здесь main проверяет его до вызова. Сама сигнатура с u32 этого правила не доказывает. Не вызывайте remaining(2, 7): не превращайте ошибочные данные в нулевой остаток, скрывая проблему. Позже научимся явно возвращать отсутствие результата или ошибку; сейчас этот договор ограничивает пример.
Ранний возврат
return завершает текущий вызов функции немедленно, не весь процесс и не только блок if. После него указывается возвращаемое значение. В этом отдельном примере fits отвечает, помещается ли задача в доступное время; нулевая длительность не допускается нашим правилом.
fn fits(minutes: u32, available: u32) -> bool {
if minutes == 0 {
return false;
}
minutes <= available
}
fn main() {
println!("{}", fits(0, 30));
println!("{}", fits(15, 30));
println!("{}", fits(40, 30));
}
false
true
false
Параметр available — доступное число минут. При нуле выполняется return false;; иначе результат даёт последнее сравнение без ;.
Типичная ошибка
Функция обещает u32, но из-за ; её блок даёт (). Ошибка E0308:
fn remaining(total: u32, done: u32) -> u32 {
total - done;
}
fn main() {
println!("{}", remaining(7, 2));
}
Уберите точку с запятой у последнего выражения или явно напишите return total - done;. Первый вариант для завершающего выражения короче.
Опорная карта
Аргументы → параметры → тело → возвращаемое значение → вызывающий код.
Проверьте понимание
- Выполняется ли функция при одном её объявлении?
- Что возвращает функция без
-> ...? - Что проверяет
mainперед вызовомremaining?
Сверка: нет, нужен вызов; (); выполненных задач не больше общего числа. Параметр — имя во входе объявления, аргумент — переданное при вызове значение.
Задание
Обязательное. Напишите is_short(minutes: u32) -> bool: короткой считаем задачу длительностью от 1 до 15 минут включительно. Функция должна вычислять ответ, а не печатать его. В main выведите результаты трёх вызовов с 0, 15, 16: false, true, false.
Самопроверка. Где параметр, где аргумент, где возвращаемый тип? Дополнительно вызовите функцию с 1: ожидается true. Не создавайте отдельную функцию на каждый случай.
Подсказка
Объедините сравнения minutes > 0 и minutes <= 15 с помощью &&. Не ставьте ; после последнего выражения функции. В main используйте пустые {} в println! для полученного ответа.
Эталон после самостоятельной попытки
fn is_short(minutes: u32) -> bool {
minutes > 0 && minutes <= 15
}
fn main() {
println!("{}", is_short(0));
println!("{}", is_short(15));
println!("{}", is_short(16));
}
false
true
false
Если вы нашли ошибку или опечатку в тексте статьи, то сообщите нам об этом
Комментарии (0)
Войдите, чтобы оставить комментарий →
Пока нет комментариев. Будьте первым.