enum în Rust

Definește un tip care poate fi exact una dintre mai multe variante; fiecare variantă poate avea propriile date.

Sintaxă
enum Nume {
    Varianta1,
    Varianta2(Tip),
    Varianta3 { camp: Tip },
}
let v = Nume::Varianta2(valoare);

Ce face enum

Un enum enumeră toate formele posibile ale unei valori. Spre deosebire de enum-urile din C sau Java, fiecare variantă poate purta date diferite: Mesaj::Text(String), Mesaj::Locatie { lat: f64, lon: f64 }, Mesaj::Iesire. O valoare de tipul enum este întotdeauna exact una dintre variante.

Valoarea se folosește cu match, care obligă să tratezi fiecare variantă și scoate datele din ea. Poți adăuga metode cu impl, ca la structuri. Două enum-uri din biblioteca standard sunt peste tot: Option<T> (Some(x) sau None, în locul lui null) și Result<T, E> (Ok(x) sau Err(e), în locul excepțiilor).

Un enum doar cu variante fără date funcționează ca în C: fiecare variantă are un număr (implicit 0, 1, 2…, sau dat explicit cu = 3), obținut cu as i32.

Exemple

Exemplul 1: Variante cu date diferite

Fiecare comandă dintr-un magazin online este într-una dintre trei stări, iar fiecare stare are datele ei. Metoda descriere folosește match ca să trateze toate stările.

enum Stare {
    InAsteptare,
    Expediata { curier: String, awb: u64 },
    Livrata(String),
}

impl Stare {
    fn descriere(&self) -> String {
        match self {
            Stare::InAsteptare => "în așteptare".to_string(),
            Stare::Expediata { curier, awb } => format!("expediată prin {curier}, AWB {awb}"),
            Stare::Livrata(data) => format!("livrată pe {data}"),
        }
    }
}

fn main() {
    let comenzi = vec![
        (1001, Stare::Livrata("12.10.2026".to_string())),
        (1002, Stare::Expediata { curier: "FAN".to_string(), awb: 2_345_678_901 }),
        (1003, Stare::InAsteptare),
    ];
    for (nr, stare) in &comenzi {
        println!("Comanda {nr}: {}", stare.descriere());
    }
}

Rezultat

Comanda 1001: livrată pe 12.10.2026
Comanda 1002: expediată prin FAN, AWB 2345678901
Comanda 1003: în așteptare

Exemplul 2: Option: valoare care poate lipsi

Option este un enum obișnuit cu variantele Some și None. Funcția returnează None când elevul nu există, iar apelantul este obligat să trateze cazul.

fn nota(elev: &str) -> Option<u8> {
    match elev {
        "Ana" => Some(10),
        "Mihai" => Some(8),
        _ => None,
    }
}

fn main() {
    for elev in ["Ana", "Radu"] {
        match nota(elev) {
            Some(n) => println!("{elev}: {n}"),
            None => println!("{elev}: nu are notă"),
        }
    }
    println!("{:?} {:?}", nota("Mihai"), nota("Ioana"));
}

Rezultat

Ana: 10
Radu: nu are notă
Some(8) None

Exemplul 3: Enum ca în C, cu valori numerice

Variantele au valori explicite. as u8 dă numărul, iar #[derive(PartialEq, PartialOrd)] permite compararea lor.

#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
enum Prioritate {
    Scazuta = 1,
    Medie = 5,
    Urgenta = 10,
}

fn main() {
    let p = Prioritate::Medie;
    println!("{p:?} = {}", p as u8);
    println!("Urgentă > medie: {}", Prioritate::Urgenta > p);
    println!("Scăzută == medie: {}", Prioritate::Scazuta == p);
}

Rezultat

Medie = 5
Urgentă > medie: true
Scăzută == medie: false

Greșeli frecvente

  • Scrii varianta fără numele enum-ului (Livrata(...) în loc de Stare::Livrata(...)). Fără use Stare::*;, variantele trebuie prefixate.
  • Folosești valori „speciale” (-1, șir gol) pentru „lipsă”, ca în alte limbaje. În Rust, pentru o valoare care poate lipsi folosește Option, iar pentru operații care pot eșua Result.
  • Compari variante cu == fără #[derive(PartialEq)]. Pentru o simplă verificare a variantei, matches!(stare, Stare::InAsteptare) funcționează și fără PartialEq.

Comenzi înrudite

  • match: Compară o valoare cu o listă de tipare și execută ramura primului care se potrivește. Compilatorul verifică dacă toate cazurile sunt tratate.
  • struct: Definește un tip nou care grupează mai multe valori cu nume (câmpuri), de exemplu un elev cu nume, clasă și medie.
  • impl, self și Self: impl adaugă metode și funcții asociate unui tip; self este valoarea pe care se apelează metoda, iar Self este tipul însuși.
  • 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.
  • matches!: Returnează true dacă o valoare se potrivește cu un tipar (ca într-o ramură de match) și false altfel.