Уроки модуля (2/2)
Обработка ошибок и ? Оператор
Философия Rust призывает вас явно распознавать возможность ошибок и структурировать свой код для их обработки до компиляции программы.
Ошибки в Rust делятся на две основные категории: неустранимые (которые вызывают немедленное завершение с помощью макроса panic!) и устранимые ошибки (управляемые с помощью типа Result<T, E>).
Тип Result<T, E> и сопоставление с шаблоном
Большинство устранимых ошибок возвращают тип Result<T, E>, который представляет собой перечисление, определенное как:
enum Result<T, E> {
Ok(T),
Err(E),
}Мы можем использовать сопоставление с образцом для проверки результата:
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(...).
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), чтобы обеспечить удобочитаемое представление ошибки:
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. Чтение файла с расширением ? оператор
Завершите функцию read_username_from_file, чтобы она использовала символ ? оператор для распространения ошибок, возникающих при открытии файла и чтении его содержимого в строку имени пользователя.
Показать подсказку
Добавьте символ `?`после`File::open(...)`и после`username_file.read_to_string(...)` для распространения ошибок.
Решение доступно после 3 попыток
Упражнение 2. Анализ и удвоение числа
Напишите функцию с именем parse_and_double, которая принимает строковую ссылку &str и возвращает Result<i32, std::num::ParseIntError>. Функция должна проанализировать строку, используя val.parse::<i32>() с ? для распространения ошибок синтаксического анализа и в случае успеха вернуть удвоенное значение, завернутое в Ok.
Показать подсказку
Используйте `val.parse::<i32>()?`, чтобы проанализировать строку и распространить ошибку, затем верните `Ok(num \* 2)`.
Решение доступно после 3 попыток
Упражнение 3. Определение пользовательской ошибки
Определите пустую структуру с именем CustomError. Реализуйте для него черту std::fmt::Display, написав «Si e verificato un errore Personalizzato» в качестве сообщения об ошибке в методе fmt.
Показать подсказку
Объявите `struct CustomError;`и реализуйте Display, написав`write!(f, "Si e verificato un errore Personalizzato")`внутри метода`fmt`.
Решение доступно после 3 попыток