panic::catch_unwind în Rust

Rulează o funcție și prinde panica ei, returnând Err în loc să oprească programul.

Sintaxă
use std::panic;

pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R, Box<dyn Any + Send + 'static>>

Ce face panic::catch_unwind

catch_unwind apelează funcția dată. Dacă funcția se termină normal, returnează Ok(rezultat). Dacă funcția intră în panică, panica este oprită aici și primești Err(încărcătura), iar programul continuă.

Nu este un mecanism de tratare a erorilor obișnuite (pentru asta există Result). Se folosește la granițe: un server care nu vrea să se oprească din cauza unei cereri, cod apelat din C, rularea unor pluginuri. Mesajul panicii este tot afișat pe ecran de „hook”-ul implicit; îl poți schimba cu panic::set_hook.

Funcționează doar când panica face unwinding (comportamentul implicit). Cu panic = "abort" în Cargo.toml, programul se oprește oricum.

Parametri

Parametru Descriere
f Funcția (de obicei un closure) rulată protejat.

Ce returnează

Ok(R) dacă funcția s-a terminat normal; Err(Box<dyn Any + Send>) cu încărcătura panicii (de obicei un &str sau un String).

Exemple

Exemplul 1: Programul continuă după panică

Am înlocuit hook-ul de panică cu unul care afișează doar mesajul, pe o singură linie. Din încărcătură scoatem textul cu downcast_ref.

use std::panic;

fn proceseaza(comanda: u32) -> u32 {
    if comanda == 2 {
        panic!("comanda {comanda} are date corupte");
    }
    comanda * 100
}

fn main() {
    panic::set_hook(Box::new(|info| {
        println!("  [hook] panică: {}", info.payload_as_str().unwrap_or("?"));
    }));

    for c in 1..=3 {
        let r = panic::catch_unwind(|| proceseaza(c));
        match r {
            Ok(v) => println!("comanda {c}: OK, {v} lei"),
            Err(incarcatura) => {
                let mesaj = incarcatura
                    .downcast_ref::<String>()
                    .map(String::as_str)
                    .or_else(|| incarcatura.downcast_ref::<&str>().copied())
                    .unwrap_or("necunoscut");
                println!("comanda {c}: eșuată ({mesaj})");
            }
        }
    }
    println!("Programul a ajuns la final.");
}

Rezultat

comanda 1: OK, 100 lei
  [hook] panică: comanda 2 are date corupte
comanda 2: eșuată (comanda 2 are date corupte)
comanda 3: OK, 300 lei
Programul a ajuns la final.

Exemplul 2: Fără hook propriu

Hook-ul implicit afișează mesajul obișnuit de panică (pe ieșirea de eroare), dar programul merge mai departe. Numărul firului diferă la fiecare rulare.

use std::panic;

fn main() {
    let r = panic::catch_unwind(|| {
        let v = [1, 2, 3];
        let i = v.len() + 4;
        v[i]
    });
    println!("prins: {}", r.is_err());
    println!("Programul continuă.");
}

Rezultat posibil (diferă la fiecare rulare)


thread 'main' (52011) panicked at src/main.rs:7:9:
index out of bounds: the len is 3 but the index is 7
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
prins: true
Programul continuă.

Greșeli frecvente

  • Folosești catch_unwind ca pe un try/catch pentru erori obișnuite. În Rust, erorile așteptate se returnează cu Result.
  • Prinzi o panică și continui să folosești datele pe care funcția le modifica: pot fi rămas pe jumătate actualizate. De aceea închiderea trebuie să fie UnwindSafe; pentru referințe mutabile compilatorul cere AssertUnwindSafe.
  • Te aștepți să prinzi o panică atunci când proiectul este compilat cu panic = "abort": în acest caz programul se oprește imediat.

Comenzi înrudite

  • Option::unwrap: Scoate valoarea din Some; pentru None programul se oprește cu panică. expect face la fel, cu mesajul tău.
  • Result::unwrap: Scoate valoarea din Ok; pentru Err oprește programul cu panică. unwrap_err face invers: scoate eroarea.
  • process::exit: Oprește imediat programul cu codul de ieșire dat (0 = succes, altceva = eroare), fără să mai ruleze restul codului.
  • thread::spawn: Pornește un fir de execuție nou care rulează funcția dată; join() așteaptă terminarea lui și îi dă rezultatul.