Result în Rust

Tipul pentru operații care pot eșua: Ok(valoare) la succes sau Err(eroare) cu motivul eșecului.

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

fn is_ok(&self) -> bool
fn is_err(&self) -> bool

Ce face Result

Rust nu are excepții. O funcție care poate eșua (citirea unui fișier, transformarea unui text în număr, o conexiune) returnează Result<T, E>: Ok(valoare) dacă a reușit, Err(eroare) dacă nu. T este tipul rezultatului, iar E tipul erorii.

Compilatorul te obligă să te uiți la rezultat: un Result ignorat produce avertismentul unused_must_use. Îl tratezi cu match, cu if let, cu metode precum unwrap_or și map_err sau îl trimiți mai departe cu operatorul ?.

is_ok() și is_err() răspund doar cu true/false, fără să scoată valoarea.

Parametri

Parametru Descriere
T Tipul valorii la succes.
E Tipul erorii: un text, std::io::Error, ParseIntError sau un tip propriu.

Ce returnează

is_ok(): true pentru Ok. is_err(): true pentru Err.

Exemple

Exemplul 1: O funcție care poate eșua

Împărțirea la zero devine o eroare normală, tratată cu match, nu o oprire a programului.

fn imparte(suma: f64, persoane: u32) -> Result<f64, String> {
    if persoane == 0 {
        return Err(String::from("nu poți împărți la 0 persoane"));
    }
    Ok(suma / persoane as f64)
}

fn main() {
    for persoane in [4, 0] {
        match imparte(250.0, persoane) {
            Ok(parte) => println!("{persoane} persoane: {parte:.2} lei de persoană"),
            Err(e) => println!("Eroare: {e}"),
        }
    }
}

Rezultat

4 persoane: 62.50 lei de persoană
Eroare: nu poți împărți la 0 persoane

Exemplul 2: Result din biblioteca standard

parse returnează Result. Cu {:?} vezi și tipul de eroare.

fn main() {
    let texte = ["42", "-7", "patru"];
    for t in texte {
        let r: Result<i32, _> = t.parse::<i32>();
        println!("{t:>6}: is_ok = {:5}, is_err = {:5}, {r:?}", r.is_ok(), r.is_err());
    }
}

Rezultat

    42: is_ok = true , is_err = false, Ok(42)
    -7: is_ok = true , is_err = false, Ok(-7)
 patru: is_ok = false, is_err = true , Err(ParseIntError { kind: InvalidDigit })

Exemplul 3: Operatorul ? trimite eroarea mai departe

Dacă oricare parse eșuează, ? returnează imediat eroarea din aduna.

use std::num::ParseIntError;

fn aduna(a: &str, b: &str) -> Result<i32, ParseIntError> {
    let x: i32 = a.trim().parse()?;
    let y: i32 = b.trim().parse()?;
    Ok(x + y)
}

fn main() {
    println!("{:?}", aduna("10", " 32"));
    println!("{:?}", aduna("10", "trei"));
}

Rezultat

Ok(42)
Err(ParseIntError { kind: InvalidDigit })

Greșeli frecvente

  • Ignori un Result: fs::write("a.txt", "x"); dă avertismentul unused_must_use (eroare cu -D warnings). Tratează-l sau scrie explicit let _ = ...; dacă chiar nu contează.
  • Folosești ? într-o funcție care returnează (): nu compilează. Funcția trebuie să returneze Result (sau Option); și main poate returna Result<(), E>.
  • Confunzi Result cu Option: Option spune doar că lipsește ceva, Result spune și de ce.

Comenzi înrudite

  • Result::unwrap: Scoate valoarea din Ok; pentru Err oprește programul cu panică. unwrap_err face invers: scoate eroarea.
  • Result::map: map transformă valoarea din Ok și lasă eroarea neschimbată; map_err transformă eroarea și lasă valoarea neschimbată.
  • Result::and_then: and_then continuă cu următorul pas care poate eșua doar dacă cel curent a reușit; or_else încearcă o recuperare la eroare.
  • Error: Trait-ul comun al tuturor tipurilor de eroare: o eroare se poate afișa (Display, Debug) și poate avea o cauză.
  • Option: Tipul care spune „poate există o valoare, poate nu”: Some(valoare) sau None, în locul lui null din alte limbaje.