Ce face operatorul ?
? scurtează tratarea erorilor. let n = text.parse::<u32>()?; înseamnă: dacă parse a reușit (Ok(n)), continuă cu n; dacă nu (Err(e)), ieși imediat din funcție cu Err(e). Fără ? ai scrie un match cu două ramuri la fiecare pas.
? poate fi folosit doar într-o funcție care returnează un tip compatibil: Result (inclusiv main declarat -> Result<(), E>) sau Option. Pe un Option, None este returnat mai departe. Nu poți amesteca: ? pe un Option într-o funcție care returnează Result cere întâi .ok_or(...).
La propagare, ? convertește eroarea cu From::from în tipul de eroare al funcției. De aceea o funcție care returnează Result<T, Box<dyn std::error::Error>> poate folosi ? pe erori de tipuri diferite (ParseIntError, io::Error), iar pentru propriul tip de eroare implementezi From.
Exemple
Exemplul 1: Propagarea erorilor de conversie
Fiecare ? poate opri funcția. Pentru „12,abc” al doilea parse eșuează și funcția returnează eroarea, iar main o afișează.
use std::num::ParseIntError;
fn suma_text(text: &str) -> Result<i32, ParseIntError> {
let mut suma = 0;
for parte in text.split(',') {
let n: i32 = parte.trim().parse()?;
suma += n;
}
Ok(suma)
}
fn main() {
for intrare in ["10, 20, 12", "12,abc", ""] {
match suma_text(intrare) {
Ok(s) => println!("„{intrare}” -> {s}"),
Err(e) => println!("„{intrare}” -> eroare: {e}"),
}
}
}
Rezultat
„10, 20, 12” -> 42
„12,abc” -> eroare: invalid digit found in string
„” -> eroare: cannot parse integer from empty string
Exemplul 2: ? cu Option
Funcția returnează Option, deci ? poate fi folosit pe split_once și pe get: dacă oricare dă None, funcția returnează None.
fn domeniu(email: &str) -> Option<&str> {
let (_, dupa) = email.split_once('@')?;
let punct = dupa.rfind('.')?;
dupa.get(punct + 1..).filter(|tld| !tld.is_empty())
}
fn main() {
for e in ["ana@exemplu.ro", "fara-arond.ro", "dan@localhost", "x@site."] {
println!("{e}: {:?}", domeniu(e));
}
}
Rezultat
ana@exemplu.ro: Some("ro")
fara-arond.ro: None
dan@localhost: None
x@site.: None
Exemplul 3: Erori de tipuri diferite cu Box
Funcția poate eșua la citirea fișierului (io::Error) sau la conversie (ParseFloatError). Cu Box<dyn Error>, ? le convertește automat pe amândouă. Fișierul este creat chiar de program, în directorul curent.
use std::error::Error;
use std::fs;
fn total_din_fisier(cale: &str) -> Result<f64, Box<dyn Error>> {
let continut = fs::read_to_string(cale)?;
let mut total = 0.0;
for linie in continut.lines() {
total += linie.trim().parse::<f64>()?;
}
Ok(total)
}
fn main() -> Result<(), Box<dyn Error>> {
fs::write("preturi.txt", "12.5\n7.25\n30\n")?;
println!("Total: {} lei", total_din_fisier("preturi.txt")?);
fs::write("gresit.txt", "12.5\ndoi lei\n")?;
if let Err(e) = total_din_fisier("gresit.txt") {
println!("gresit.txt: {e}");
}
if let Err(e) = total_din_fisier("lipsa.txt") {
println!("lipsa.txt: {e}");
}
Ok(())
}
Rezultat
Total: 49.75 lei
gresit.txt: invalid float literal
lipsa.txt: No such file or directory (os error 2)
Greșeli frecvente
- Folosești
?într-o funcție care returnează()(de exemplu unmainobișnuit): eroarea E0277the `?` operator can only be used in a function that returns `Result` or `Option`. Schimbă tipul returnat. - Aplici
?pe unOptionîntr-o funcție cuResult. Transformă întâi:valoare.ok_or("lipsește")?. - Pui
unwrap()peste tot în loc de?.unwrapoprește programul cu panică la prima eroare;?lasă apelantul să decidă ce face.
Comenzi înrudite
main: Funcția cu care începe orice program Rust: este apelată automat la pornire, iar programul se termină când ea se termină.if let și let else: if let execută un bloc doar dacă o valoare se potrivește cu un tipar; let else desface valoarea sau iese imediat din funcție.enum: Definește un tip care poate fi exact una dintre mai multe variante; fiecare variantă poate avea propriile date.type (alias de tip și tip asociat): Dă un nume nou unui tip existent (alias), ca să scurtezi tipuri lungi; în trait-uri, declară un tip asociat.match: Compară o valoare cu o listă de tipare și execută ramura primului care se potrivește. Compilatorul verifică dacă toate cazurile sunt tratate.