0Pricing
Learn Rust Coding · Lekcja

Solidna obsługa błędów za pomocą `Result`

Zaimplementują Państwo idiomatyczną obsługę błędów z użyciem enuma `Result`, operatora `?` i własnych typów błędów, tworząc odporne aplikacje.

Solidna obsługa błędów za pomocą `Result` to bezpłatna lekcja Learn Rust Coding na CoddyKit. To lekcja 3 z 3. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Learn Rust Coding, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Learn Rust Coding zawiera 3 lekcji w sumie.

Obsługa błędów w Rust

Błędy są częścią programowania. W Rust rozróżniamy dwa główne typy: błędy możliwe do obsłużenia i nieodwracalne.

  • Błędy nieodwracalne zwykle sygnalizują błędy w programie i powodują jego zatrzymanie za pomocą panic!.
  • Błędy możliwe do obsłużenia oznaczają, że coś poszło nie tak, ale można sobie z tym poradzić, na przykład gdy nie znaleziono pliku.

Rust pomaga w elegancki sposób obsługiwać błędy możliwe do naprawienia.

`panic!` a `Result`

Gdy wystąpi błąd nieodwracalny, Rust wywołuje makro panic!. Rozwija ono stos i kończy działanie programu, zwykle wyświetlając komunikat o błędzie.

Do obsługi błędów możliwych do naprawienia Rust używa enuma Result. Ten enum informuje, czy operacja zakończyła się powodzeniem (Ok), czy niepowodzeniem (Err), dzięki czemu program może zdecydować, co zrobić dalej.

Enum `Result`

Enum Result można opisać w uproszczeniu następująco:

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

  • T oznacza typ wartości zwracanej w przypadku powodzenia.
  • E oznacza typ błędu zwracanego w przypadku niepowodzenia.

To potężny sposób na jawne zaznaczenie, że funkcja może zakończyć się niepowodzeniem.

Dopasowywanie do `Result`

Najczęstszym sposobem obsługi Result jest użycie wyrażenia match. Umożliwia ono wykonanie różnych bloków kodu w zależności od tego, czy Result ma wartość Ok, czy Err.

Proszę uruchomić ten przykład, który próbuje sparsować liczbę:

fn parse_and_add(text: &str) -> Result<i32, std::num::ParseIntError> {
    text.parse::<i32>()
}

fn main() {
    let num_str = "123";
    let result = parse_and_add(num_str);

    match result {
        Ok(num) => println!("Parsed number: {}", num),
        Err(e) => println!("Error parsing: {}", e),
    }

    let bad_str = "abc";
    let bad_result = parse_and_add(bad_str);

    match bad_result {
        Ok(num) => println!("Parsed number: {}", num),
        Err(e) => println!("Error parsing: {}", e),
    }
}

Ostrożne rozpakowywanie wartości `Result`

Czasami można mieć pewność, że operacja się powiedzie, albo chcieć, aby program zakończył działanie w przypadku błędu. Metody takie jak unwrap() i expect() wyodrębniają wartość Ok.

  • unwrap(): zwraca wartość Ok albo wywołuje panic, jeśli otrzyma Err.
  • expect("message"): działa tak samo jak unwrap(), ale umożliwia podanie własnego komunikatu panic.

Należy używać ich oszczędnie, głównie w testach lub wtedy, gdy błąd rzeczywiście jest nieodwracalny.

Wprowadzenie do operatora `?`

Pisanie instrukcji match dla każdego Result może być powtarzalne. Operator ? zapewnia zwięzły skrót ułatwiający propagowanie błędów.

Umieszczony za wyrażeniem zwracającym Result, operator ? wykonuje dwie czynności:

  • Jeśli Result ma wartość Err, natychmiast zwraca Err z bieżącej funkcji.
  • Jeśli Result ma wartość Ok, rozpakowuje wartość Ok i kontynuuje wykonywanie programu.

Operator `?` w praktyce

Operator ? znacznie upraszcza pisanie funkcji, które mogą zakończyć się niepowodzeniem. Należy pamiętać, że funkcja używająca operatora ? sama musi zwracać Result (lub Option).

Proszę zobaczyć, jak operator ? upraszcza obsługę błędów w przykładzie odczytu pliku:

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

fn read_username_from_file() -> Result<String, io::Error> {
    let mut f = File::open("hello.txt")?; // Propagates error if file not found
    let mut s = String::new();
    f.read_to_string(&mut s)?; // Propagates error if read fails
    Ok(s)
}

fn main() {
    // Create a dummy file for the example to work
    // In a real scenario, this file might not exist
    std::fs::write("hello.txt", "CoddyKit User").unwrap();

    match read_username_from_file() {
        Ok(username) => println!("Username: {}", username),
        Err(e) => println!("Error reading username: {}", e),
    }

    // Clean up the dummy file
    std::fs::remove_file("hello.txt").unwrap();
}

Własne typy błędów

Czasami wbudowane typy błędów nie są wystarczająco szczegółowe. Można definiować własne typy błędów za pomocą enumów, aby reprezentować różne sposoby, na jakie aplikacja może zakończyć się niepowodzeniem.

Dzięki temu komunikaty o błędach są bardziej zrozumiałe, a kod wywołujący może różnie obsługiwać poszczególne błędy.

Popularnym wzorcem jest utworzenie enuma zawierającego wszystkie błędy, które może zwrócić dana funkcja lub moduł.

Dostosowanie `?` do własnych błędów

Aby operator ? automatycznie konwertował błąd niższego poziomu (taki jak io::Error) na własny typ błędu, własny enum błędów musi implementować trait From dla tego błędu niższego poziomu.

Informuje to Rust, jak konwertować jeden typ błędu na inny, dzięki czemu propagowanie błędów przebiega płynnie.

use std::io;
use std::fs::File;
use std::io::Read;

#[derive(Debug)]
enum MyError {
    Io(io::Error),
    Parse(std::num::ParseIntError)
}

impl From<io::Error> for MyError {
    fn from(error: io::Error) -> Self {
        MyError::Io(error)
    }
}

impl From<std::num::ParseIntError> for MyError {
    fn from(error: std::num::ParseIntError) -> Self {
        MyError::Parse(error)
    }
}

fn read_config(path: &str) -> Result<i32, MyError> {
    let mut file = File::open(path)?; // io::Error converted to MyError::Io
    let mut contents = String::new();
    file.read_to_string(&mut contents)?; // io::Error converted to MyError::Io
    let value = contents.trim().parse::<i32>()?; // ParseIntError converted to MyError::Parse
    Ok(value)
}

fn main() {
    // Create a dummy file for the example
    std::fs::write("config.txt", "123").unwrap();

    match read_config("config.txt") {
        Ok(val) => println!("Config value: {}", val),
        Err(e) => println!("Failed to read config: {:?}", e),
    }

    std::fs::remove_file("config.txt").unwrap(); // Clean up

    // Example with a non-existent file
    match read_config("non_existent.txt") {
        Ok(val) => println!("Config value: {}", val),
        Err(e) => println!("Failed to read config: {:?}", e),
    }
}

Sprawdź swoją wiedzę

Proszę rozważyć poniższy fragment kodu Rust. Jaki będzie wynik, jeśli plik data.txt NIE istnieje?

Podsumowanie: niezawodna obsługa błędów

Opanował(a) Pan/Pani niezawodną obsługę błędów w Rust!

  • Enum Result: rozróżnia Ok(T) oznaczające powodzenie i Err(E) oznaczające niepowodzenie.
  • match: podstawowy sposób obsługi wariantów Result.
  • Operator ?: potężny skrót do propagowania wartości Err w górę stosu wywołań.
  • Własne błędy: umożliwiają definiowanie własnych typów błędów za pomocą enumów, zapewniając przejrzystość i szczegółową obsługę.
  • Trait From: podczas używania ? umożliwia płynną konwersję błędów niższego poziomu na własne typy błędów.

W następnej kolejności poznają Państwo zaawansowane funkcje systemu typów!

Często zadawane pytania

Czy lekcja „Solidna obsługa błędów za pomocą `Result`” jest bezpłatna?

Tak — pełny tekst „Solidna obsługa błędów za pomocą `Result`” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Learn Rust Coding, przejdź na CoddyKit PRO. Kurs Learn Rust Coding zawiera 3 lekcji w sumie.

Co nauczysz się w „Solidna obsługa błędów za pomocą `Result`”?

Zaimplementują Państwo idiomatyczną obsługę błędów z użyciem enuma `Result`, operatora `?` i własnych typów błędów, tworząc odporne aplikacje. Ćwiczysz Learn Rust Coding z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Learn Rust Coding?

Nie wymagamy żadnego doświadczenia. Learn Rust Coding w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 3.

Ile czasu zajmuje lekcja „Solidna obsługa błędów za pomocą `Result`”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Learn Rust Coding?

Tak. Każda lekcja Learn Rust Coding zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Organizowanie kodu za pomocą modułów
  2. Zarządzanie zależnościami za pomocą crates
  3. Solidna obsługa błędów za pomocą `Result`
← Powrót do Learn Rust Coding