Lekcje modułu (2/2)
Obsługa błędów i operator ?
Filozofia Rusta zachęca do jawnego rozpoznawania możliwości wystąpienia błędów i konstruowania kodu tak, aby obsługiwał je przed skompilowaniem programu.
Błędy w Rust są podzielone na dwie główne kategorie: błędy nienaprawialne (powodujące natychmiastowe zawieszenie poprzez panikę panic!) i błędy naprawialne (obsługiwane przez typ Result<T, E>).
Typ wyniku<T, E> i dopasowanie wzorca
Większość możliwych do naprawienia błędów zwraca typ Result<T, E>, który jest wyliczeniem zdefiniowanym jako:
enum Result<T, E> {
Ok(T),
Err(E),
}
Możemy użyć dopasowywania wzorców, aby sprawdzić wynik:
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!("Problema nell'aprire il file: {:?}", error),
};
}
Propagacja błędów za pomocą operatora ?
Kiedy implementujemy funkcję, zamiast bezpośrednio w niej obsługiwać błąd, często chcemy zwrócić błąd osobie wywołującej, aby mógł zdecydować, co zrobić. Proces ten nazywany jest propagacją błędów.
Rust udostępnia operator ? jako skrót składniowy do propagacji błędów. Jeśli wartość Result wynosi Ok, wartość w obrębie Ok jest zwracana przez wyrażenie; jeśli jest to Err, cała bieżąca funkcja zwraca błąd Err, tak jakbyśmy użyli 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)
}
[!WAŻNE] Operatora
?można używać tylko w funkcjach zwracających typ zgodny z wartością, do której jest zastosowany (zwykleResult,Optionlub typy implementująceFromResidual).
Błędy niestandardowe
Możemy zdefiniować własne typy błędów, implementując cechę std::fmt::Display (i opcjonalnie std::error::Error), aby zapewnić czytelną dla człowieka reprezentację błędu:
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, "Errore: {}", self.details)
}
}
impl std::error::Error for MyError {}
Spróbuj sam
Ćwiczenie 1: Odczyt pliku z ? operator
Wypełnij funkcję read_username_from_file, aby użyć ? operator. do propagowania błędów generowanych przez otwarcie pliku i wczytanie jego zawartości do ciągu nazwy użytkownika.
Pokaż wskazówkę
Dodaj znak `?` po`File::open(...)`i po`username_file.read_to_string(...)`, aby propagować błędy.
Rozwiązanie dostępne po 3 próbach
Ćwiczenie 2: Analiza i przetwarzanie liczby
Napisz funkcję o nazwie parse_and_double, która przyjmuje jako dane wejściowe odwołanie do ciągu znaków &str i zwraca Result<i32, std::num::ParseIntError>. Funkcja powinna przeanalizować ciąg za pomocą val.parse::<i32>() przy użyciu ? aby propagować błąd analizy i jeśli się powiedzie, zwróć podwojoną wartość ujętą w OK.
Pokaż wskazówkę
Użyj `val.parse::<i32>()?`, aby przekonwertować ciąg poprzez propagację błędu, a następnie zwróć`Ok(num * 2)`.
Rozwiązanie dostępne po 3 próbach
Ćwiczenie 3: Zdefiniuj błąd niestandardowy
Zdefiniuj pustą strukturę o nazwie CustomError. Zaimplementuj dla niego cechę std::fmt::Display, wpisując komunikat „Wystąpił błąd niestandardowy” wewnątrz metody fmt.
Pokaż wskazówkę
Zadeklaruj `struct CustomError;` i zaimplementuj Display, wpisując `write!(f, "Si e verificato un errore personalizzato")`w metodzie`fmt`.
Rozwiązanie dostępne po 3 próbach