panic! în Rust

Oprește firul de execuție curent cu un mesaj de eroare. Se folosește pentru situații care nu ar trebui să apară niciodată (bug-uri).

Sintaxă
panic!();
panic!("mesaj");
panic!("{} nu este valid", valoare);

Ce face panic!

panic! semnalează o eroare din care programul nu își poate reveni în mod normal: o greșeală de programare, o stare imposibilă. Afișează pe ieșirea de eroare thread 'main' panicked at fisier:linie:coloana: urmat de mesaj, eliberează valorile de pe stivă (apelează drop) și oprește firul. Dacă firul este main, programul se termină cu codul 101.

Multe operații dau panică intern: unwrap() pe None sau Err, indicele în afara unui vector, împărțirea întreagă la zero, depășirea unui întreg în modul debug. Variabila de mediu RUST_BACKTRACE=1 afișează și lanțul de apeluri care a dus la panică.

Pentru erori așteptate (fișier inexistent, date greșite de la utilizator) folosește Result, nu panic!: apelantul poate decide ce face. O panică dintr-un fir secundar nu oprește programul: join() returnează Err. std::panic::catch_unwind poate prinde o panică, dar nu este un mecanism de excepții pentru uz obișnuit.

Exemple

Exemplul 1: Panică pentru o stare imposibilă

Programul afișează primul calcul, apoi ajunge la o lună invalidă și se oprește. Mesajul conține fișierul, linia și coloana unde a fost apelat panic!. Numărul dintre paranteze identifică firul și diferă la fiecare rulare.

fn zile_in_luna(luna: u32) -> u32 {
    match luna {
        1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
        4 | 6 | 9 | 11 => 30,
        2 => 28,
        _ => panic!("luna {luna} nu există"),
    }
}

fn main() {
    println!("Aprilie are {} zile", zile_in_luna(4));
    println!("Luna 13 are {} zile", zile_in_luna(13));
}

Rezultat (programul se oprește cu panică)

Aprilie are 30 zile

thread 'main' (14809654) panicked at main.rs:6:14:
luna 13 nu există
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Exemplul 2: Panica dintr-un fir secundar nu oprește programul

Un panic hook propriu înlocuiește mesajul implicit cu unul scurt. Firul care dă panică se oprește, dar main continuă: join() returnează Err, iar celălalt fir își termină treaba.

use std::panic;
use std::thread;

fn main() {
    panic::set_hook(Box::new(|info| {
        let mesaj = info.payload().downcast_ref::<String>().map(|s| s.as_str())
            .or_else(|| info.payload().downcast_ref::<&str>().copied())
            .unwrap_or("?");
        println!("  (panică: {mesaj})");
    }));

    let bun = thread::spawn(|| 10 + 20);
    let rau = thread::spawn(|| -> i32 { panic!("fișierul de configurare lipsește") });

    println!("fir rău: {:?}", rau.join().is_err());
    println!("fir bun: {:?}", bun.join());
    println!("main continuă normal");
}

Rezultat

  (panică: fișierul de configurare lipsește)
fir rău: true
fir bun: Ok(30)
main continuă normal

Greșeli frecvente

  • Folosești panic! (sau unwrap()) pentru erori obișnuite, ca un fișier care lipsește. Programul se oprește brusc; returnează un Result și lasă apelantul să decidă.
  • Folosești catch_unwind ca pe try/catch. Nu toate panicile pot fi prinse (cu panic = "abort" în Cargo.toml programul se oprește imediat), iar codul devine greu de urmărit.
  • Dai panică într-un drop în timpul altei panici: programul este oprit imediat (abort), fără mesaj clar.

Comenzi înrudite

  • assert! și debug_assert!: Verifică o condiție la rulare și dă panică dacă este falsă. debug_assert! face verificarea doar în modul debug, nu și în release.
  • unreachable!: Marchează un loc din cod la care execuția nu ar trebui să ajungă niciodată; dacă ajunge totuși, dă panică.
  • todo! și unimplemented!: Țin locul unui cod nescris: programul se compilează, dar dacă se ajunge acolo, dă panică cu „not yet implemented”.
  • operatorul ?: Pus după un Result sau Option: dă valoarea dacă totul e bine, altfel returnează imediat eroarea (sau None) din funcția curentă.
  • eprintln! și eprint!: Ca println! și print!, dar scriu pe ieșirea de eroare (stderr), separată de rezultatul normal al programului.