Перейти к основному содержимому
Модуль 7 · Урок 2 из 214/14 в курсе~15 min
Уроки модуля (2/2)

Обработка ошибок и ? Оператор

Философия Rust призывает вас явно распознавать возможность ошибок и структурировать свой код для их обработки до компиляции программы.

Ошибки в Rust делятся на две основные категории: неустранимые (которые вызывают немедленное завершение с помощью макроса panic!) и устранимые ошибки (управляемые с помощью типа Result<T, E>).


Тип Result<T, E> и сопоставление с шаблоном

Большинство устранимых ошибок возвращают тип Result<T, E>, который представляет собой перечисление, определенное как:

Rust
enum Result<T, E> {
    Ok(T),
    Err(E),
}

Мы можем использовать сопоставление с образцом для проверки результата:

Rust
use std::fs::File;

fn main() {
    let greeting_file_result = File::open("hello.txt");

    let greeting_file = match greeting_file_result {
        Ok(file) => file,
        Err(error) => panic!("Problem opening the file: {:?}", error),
    };
}

Распространение ошибок с помощью оператора ?

При реализации функции вместо обработки ошибки непосредственно внутри нее мы часто хотим вернуть ошибку вызывающему объекту, чтобы он мог решить, что делать. Этот процесс называется распространением ошибок.

Rust предоставляет оператор ? в качестве синтаксического ярлыка для распространения ошибок. Если значение Result равно Ok, внутреннее значение Ok возвращается выражением; если это Err, ошибка Err возвращается из всей текущей функции, как если бы мы использовали return Err(...).

Rust
use std::fs::File;
use std::io::{self, Read};

fn read_username_from_file() -> Result<String, io::Error> {
    let mut username_file = File::open("username.txt")?;
    let mut username = String::new();
    username_file.read_to_string(&mut username)?;
    Ok(username)
}

[!ВАЖНО] Оператор ? можно использовать только в функциях, которые возвращают тип, совместимый со значением, к которому он применяется (обычно Result, Option или типы, реализующие FromResidual).


Пользовательские ошибки

Мы можем определить наши собственные типы ошибок, реализовав признак std::fmt::Display (и, возможно, std::error::Error), чтобы обеспечить удобочитаемое представление ошибки:

Rust
use std::fmt;

#[derive(Debug)]
struct MyError {
    details: String,
}

impl fmt::Display for MyError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Error: {}", self.details)
    }
}

impl std::error::Error for MyError {}

Попробуйте сами

Упражнение 1. Чтение файла с расширением ? оператор

Упражнение#rust.m7.l2.e1
Попыток: 0Загрузка…

Завершите функцию read_username_from_file, чтобы она использовала символ ? оператор для распространения ошибок, возникающих при открытии файла и чтении его содержимого в строку имени пользователя.

Загрузка редактора…
Показать подсказку

Добавьте символ `?`после`File::open(...)`и после`username_file.read_to_string(...)` для распространения ошибок.

Решение доступно после 3 попыток

Упражнение 2. Анализ и удвоение числа

Упражнение#rust.m7.l2.e2
Попыток: 0Загрузка…

Напишите функцию с именем parse_and_double, которая принимает строковую ссылку &str и возвращает Result<i32, std::num::ParseIntError>. Функция должна проанализировать строку, используя val.parse::<i32>() с ? для распространения ошибок синтаксического анализа и в случае успеха вернуть удвоенное значение, завернутое в Ok.

Загрузка редактора…
Показать подсказку

Используйте `val.parse::<i32>()?`, чтобы проанализировать строку и распространить ошибку, затем верните `Ok(num \* 2)`.

Решение доступно после 3 попыток

Упражнение 3. Определение пользовательской ошибки

Упражнение#rust.m7.l2.e3
Попыток: 0Загрузка…

Определите пустую структуру с именем CustomError. Реализуйте для него черту std::fmt::Display, написав «Si e verificato un errore Personalizzato» в качестве сообщения об ошибке в методе fmt.

Загрузка редактора…
Показать подсказку

Объявите `struct CustomError;`и реализуйте Display, написав`write!(f, "Si e verificato un errore Personalizzato")`внутри метода`fmt`.

Решение доступно после 3 попыток