unreachable! în Rust

Marchează un loc din cod la care execuția nu ar trebui să ajungă niciodată; dacă ajunge totuși, dă panică.

Sintaxă
unreachable!()
unreachable!("mesaj {}", x)

Ce face unreachable!

Uneori știi ceva ce compilatorul nu poate dovedi: de exemplu că restul împărțirii la 3 este 0, 1 sau 2. Un match pe n % 3 trebuie totuși să trateze toate valorile u32. unreachable!() acoperă cazul imposibil și documentează presupunerea.

Ca todo! și panic!, are tipul !, deci se potrivește în orice ramură. Dacă presupunerea ta este greșită și execuția ajunge acolo, programul dă panică cu internal error: entered unreachable code (și mesajul tău), în loc să continue cu date greșite.

Nu îl confunda cu std::hint::unreachable_unchecked(), o funcție unsafe care îi spune optimizatorului că locul este imposibil; dacă greșești acolo, comportamentul este nedefinit.

Exemple

Exemplul 1: Un caz pe care matematica îl exclude

an % 4 poate fi doar 0, 1, 2 sau 3. Ramura _ nu se execută niciodată, dar este necesară ca match să fie complet.

fn tip_an(an: u32) -> &'static str {
    match an % 4 {
        0 => "an cu Olimpiadă de vară",
        1 | 3 => "an fără Olimpiadă",
        2 => "an cu Olimpiadă de iarnă",
        _ => unreachable!("restul împărțirii la 4 este mereu sub 4"),
    }
}

fn main() {
    for an in [2024, 2025, 2026, 2027] {
        println!("{an}: {}", tip_an(an));
    }
}

Rezultat

2024: an cu Olimpiadă de vară
2025: an fără Olimpiadă
2026: an cu Olimpiadă de iarnă
2027: an fără Olimpiadă

Exemplul 2: După o validare făcută mai devreme

Textul este verificat întâi că are doar cifrele 1, 2 sau 3, deci în match alt caracter este imposibil. Dacă cineva ar șterge validarea, unreachable! ar semnala imediat greșeala.

fn decodeaza(cod: &str) -> Vec<&'static str> {
    assert!(cod.chars().all(|c| matches!(c, '1'..='3')), "cod invalid");
    cod.chars()
        .map(|c| match c {
            '1' => "mic",
            '2' => "mediu",
            '3' => "mare",
            _ => unreachable!(),
        })
        .collect()
}

fn main() {
    println!("{:?}", decodeaza("1323"));
}

Rezultat

["mic", "mare", "mediu", "mare"]

Greșeli frecvente

  • Folosești unreachable! pentru cazuri care chiar pot apărea (date de la utilizator, fișiere). Pentru ele returnează o eroare.
  • Pui unreachable! pe un caz pe care compilatorul îl știe deja imposibil (de exemplu după un match complet pe un enum): compilatorul avertizează unreachable pattern sau unreachable expression.

Comenzi înrudite

  • todo! și unimplemented!: Țin locul unui cod nescris: programul se compilează, dar dacă se ajunge acolo, dă panică cu „not yet implemented”.
  • panic!: 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).
  • match: Compară o valoare cu o listă de tipare și execută ramura primului care se potrivește. Compilatorul verifică dacă toate cazurile sunt tratate.
  • matches!: Returnează true dacă o valoare se potrivește cu un tipar (ca într-o ramură de match) și false altfel.