Shanraq.org Shanraq.org
Result: сообщить о неудачной операции
IT

Rust: с нуля до своего органайзера Урок 32 из 40

Result: сообщить о неудачной операции

Урок 32. Превратить текст в число минут и объяснить неверный ввод без остановки программы.

Текст переведён с помощью ИИ.

Предварительно: уроки 10, 22 и 29–31. Если Some и None смешиваются, повторите урок 31.

Образ и опорная карта

Если карточки в ящике нет, мы получаем отсутствие. Если на карточке вместо минут написано «скоро», карточка есть, но прочитать число не удалось. Result<T, E> — стандартное перечисление Rust для операции, которая может завершиться успешно или с ошибкой. Ok(T) хранит успешный результат типа T, Err(E) — сведения об ошибке типа E. Образ ограничен: компилятор проверяет, что мы обработали варианты, но не сочиняет понятное человеку сообщение об ошибке за нас.

Текст → попытка разбора → Ok(число) или Err(причина)match → ответ. В Result<u32, String> успех содержит u32, ошибка — принадлежащий программе текст String. Запятая в <u32, String> разделяет два типа. Метод .parse::<u32>() пытается прочитать из текста число u32; запись ::<u32> указывает методу нужный тип результата. Это указание типа после имени метода, а не сравнение и не создание коллекции. Сам метод возвращает Result, поэтому неверный текст не заставляет программу аварийно завершиться.

Две попытки разбора

Сохраните прежний src/main.rs проекта organizer, замените весь файл и выполните cargo run рядом с Cargo.toml. Другие файлы и зависимости не меняются. Предскажите оба сообщения.

Функция read_minutes превращает ошибку разбора в наше объяснение. Err(_) распознаёт неудачу и знакомым из урока 30 знаком _ не сохраняет технические подробности, которые мы пока не показываем читателю. Ветви match возвращают один тип Result<u32, String>. Затем main явно разбирает и успех, и ошибку.

fn read_minutes(text: &str) -> Result<u32, String> {
    match text.parse::<u32>() {
        Ok(minutes) => Ok(minutes),
        Err(_) => Err(String::from("Нужно целое число минут")),
    }
}

fn main() {
    for text in ["25", "скоро"] {
        match read_minutes(text) {
            Ok(minutes) => println!("Минут: {minutes}"),
            Err(message) => println!("Ошибка: {message}"),
        }
    }
}
Минут: 25
Ошибка: Нужно целое число минут

Здесь 0 тоже успешно разбирается как u32: синтаксически это число. Правило «минут должно быть больше нуля» — отдельная проверка смысла, её добавим в следующем уроке. Отрицательное число не подходит u32; для него функция вернёт Err.

Ошибка — значение, а не крах

Result<u32, String> нельзя выдать за готовый u32. Следующий отдельный пример отклоняется с E0308:

fn main() {
    let minutes: u32 = "25".parse::<u32>();
    println!("{minutes}");
}

У Result есть метод .unwrap(), который извлекает значение при Ok, но останавливает программу с panic при Err. Для пользовательского ввода он не подходит: неверное число — ожидаемая ситуация, которую нужно объяснить. В этом уроке обрабатывайте оба варианта через match. Option из урока 31 отвечает «есть ли значение?», а Result дополнительно несёт причину неудачи.

Проверка понимания

  1. Что означает String во втором месте Result<u32, String>?
  2. Почему "0".parse::<u32>() само по себе не ошибка?
  3. Почему .unwrap() опасен для ввода человека?

Проверьте: тип сообщения об ошибке; ноль — корректная запись числа; при Err программа остановится вместо объяснения. Восстановите опорную карту без страницы.

Задание

Обязательное. Напишите read_minutes(text: &str) -> Result<u32, String> как в примере, но с сообщением Введите число минут. Проверьте тексты "30" и "много", выведите Минут: 30 и Ошибка: Введите число минут. Число должно приходить из Ok, а сообщение — из Err.

Граница. Проверьте "0" и объясните, почему разбор успешен, хотя ноль позже может быть отклонён правилом задачи.

Подсказка

Сначала match text.parse::<u32>(), затем в main второй match над результатом функции. Не применяйте .unwrap().

Эталон после попытки

fn read_minutes(text: &str) -> Result<u32, String> {
    match text.parse::<u32>() {
        Ok(minutes) => Ok(minutes),
        Err(_) => Err(String::from("Введите число минут")),
    }
}

fn main() {
    for text in ["30", "много"] {
        match read_minutes(text) {
            Ok(minutes) => println!("Минут: {minutes}"),
            Err(message) => println!("Ошибка: {message}"),
        }
    }
}
Минут: 30
Ошибка: Введите число минут

Так программа сообщает о неверном вводе и продолжает работу. Официальный разбор Result.

Предыдущий урок · Оглавление · Следующий урок

Если вы нашли ошибку или опечатку в тексте статьи, то сообщите нам об этом

Комментарии (0)

Пока нет комментариев. Будьте первым.