Ce face Error
std::error::Error este trait-ul pe care îl implementează erorile din biblioteca standard (io::Error, ParseIntError etc.) și pe care ar trebui să-l implementeze și erorile tale. Cere două lucruri: Debug (de obicei prin #[derive(Debug)]) și Display (mesajul pentru oameni, scris de tine). Metodele trait-ului au implementări implicite, deci un impl Error for EroareaMea {} gol este suficient.
Avantajul este că orice eroare poate fi pusă într-un Box<dyn Error>. O funcție care returnează Result<T, Box<dyn Error>> poate folosi ? pe erori de tipuri diferite: fiecare este convertită automat în Box<dyn Error>. Este varianta cea mai simplă pentru programe mici și pentru main.
Ce returnează
Trait-ul nu returnează nimic; vezi source pentru cauza unei erori.
Exemple
Exemplul 1: O eroare proprie
Enum-ul descrie cazurile de eroare; Display dă mesajul afișat, iar Error o face compatibilă cu restul bibliotecii.
use std::error::Error;
use std::fmt;
#[derive(Debug)]
enum EroareComanda {
StocInsuficient { produs: String, disponibil: u32 },
ProdusNecunoscut(String),
}
impl fmt::Display for EroareComanda {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
EroareComanda::StocInsuficient { produs, disponibil } => {
write!(f, "mai sunt doar {disponibil} bucăți de {produs}")
}
EroareComanda::ProdusNecunoscut(p) => write!(f, "produsul „{p}” nu există"),
}
}
}
impl Error for EroareComanda {}
fn comanda(produs: &str, bucati: u32) -> Result<u32, EroareComanda> {
match produs {
"caiet" if bucati <= 5 => Ok(bucati * 4),
"caiet" => Err(EroareComanda::StocInsuficient { produs: produs.to_string(), disponibil: 5 }),
_ => Err(EroareComanda::ProdusNecunoscut(produs.to_string())),
}
}
fn main() {
for (p, n) in [("caiet", 3), ("caiet", 9), ("radieră", 1)] {
match comanda(p, n) {
Ok(total) => println!("Comandă OK: {total} lei"),
Err(e) => println!("Eroare: {e} (Debug: {e:?})"),
}
}
}
Rezultat
Comandă OK: 12 lei
Eroare: mai sunt doar 5 bucăți de caiet (Debug: StocInsuficient { produs: "caiet", disponibil: 5 })
Eroare: produsul „radieră” nu există (Debug: ProdusNecunoscut("radieră"))
Exemplul 2: Box pentru erori de tipuri diferite
? acceptă și io::Error, și ParseIntError, pentru că ambele se convertesc în Box<dyn Error>.
use std::error::Error;
use std::fs;
fn total_din_fisier(cale: &str) -> Result<i32, Box<dyn Error>> {
let text = fs::read_to_string(cale)?;
let mut total = 0;
for linie in text.lines() {
total += linie.trim().parse::<i32>()?;
}
Ok(total)
}
fn main() -> Result<(), Box<dyn Error>> {
fs::write("bune.txt", "10\n20\n30\n")?;
fs::write("rele.txt", "10\ndouăzeci\n")?;
println!("bune.txt: {:?}", total_din_fisier("bune.txt"));
for cale in ["rele.txt", "lipsa.txt"] {
if let Err(e) = total_din_fisier(cale) {
println!("{cale}: eroare: {e}");
}
}
Ok(())
}
Rezultat
bune.txt: Ok(60)
rele.txt: eroare: invalid digit found in string
lipsa.txt: eroare: No such file or directory (os error 2)
Greșeli frecvente
- Implementezi
ErrorfărăDisplay: nu compilează, pentru căDisplayeste cerut de trait. - Pui în
Displaytot textul de laDebug. MesajulDisplayeste pentru utilizator: scurt, fără nume de tipuri, de obicei cu literă mică și fără punct la final. - Returnezi
Box<dyn Error>dintr-o bibliotecă: apelantul nu mai poate facematchpe cazurile de eroare. Pentru biblioteci, un enum propriu este mai util.
Comenzi înrudite
Error::source: Returnează eroarea care a cauzat eroarea curentă, dacă există, ca să poți afișa tot lanțul de cauze.Result: Tipul pentru operații care pot eșua: Ok(valoare) la succes sau Err(eroare) cu motivul eșecului.Result::map: map transformă valoarea din Ok și lasă eroarea neschimbată; map_err transformă eroarea și lasă valoarea neschimbată.io::Error: Eroarea operațiilor de intrare/ieșire; kind() dă categoria (NotFound, PermissionDenied…), iar new() creează una proprie.ParseIntError: Eroarea returnată când un text nu poate fi transformat în număr întreg; kind() spune motivul exact.