00
:
00
:
00
:
00
•Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

Enum in Rust

Le enum (enumerazioni) sono uno dei tipi piu potenti di Rust. A differenza delle enum di molti altri linguaggi, le enum in Rust possono contenere dati associati a ciascuna variante, rendendole simili ai tipi algebrici (algebraic data types) dei linguaggi funzionali.

Definire un Enum

Un enum si definisce con la keyword enum e un elenco di varianti:

enum Direzione {
    Nord,
    Sud,
    Est,
    Ovest,
}

fn main() {
    let dir = Direzione::Nord;

    match dir {
        Direzione::Nord => println!("Vai verso nord"),
        Direzione::Sud => println!("Vai verso sud"),
        Direzione::Est => println!("Vai verso est"),
        Direzione::Ovest => println!("Vai verso ovest"),
    }
}

Enum con Dati Associati

La vera potenza degli enum in Rust sta nella possibilita di associare dati a ciascuna variante. Esistono tre forme di variante:

enum Messaggio {
    // Variante unitaria (senza dati)
    Esci,

    // Variante con dati posizionali (stile tupla)
    Sposta(i32, i32),

    // Variante con campi nominati (stile struct)
    Scrivi { testo: String, urgente: bool },

    // Variante con singolo dato
    CambiaColore(String),
}

fn main() {
    let msg1 = Messaggio::Esci;
    let msg2 = Messaggio::Sposta(10, 20);
    let msg3 = Messaggio::Scrivi {
        testo: String::from("Ciao!"),
        urgente: true,
    };
    let msg4 = Messaggio::CambiaColore(String::from("#FF0000"));

    gestisci_messaggio(msg2);
    gestisci_messaggio(msg3);
}

fn gestisci_messaggio(msg: Messaggio) {
    match msg {
        Messaggio::Esci => println!("Uscita dal programma"),
        Messaggio::Sposta(x, y) => println!("Spostamento a ({}, {})", x, y),
        Messaggio::Scrivi { testo, urgente } => {
            if urgente {
                println!("URGENTE: {}", testo);
            } else {
                println!("{}", testo);
            }
        }
        Messaggio::CambiaColore(colore) => println!("Nuovo colore: {}", colore),
    }
}

Matching degli Enum

Il costrutto match e il modo principale per gestire le varianti di un enum. Rust richiede che il match sia esaustivo: tutte le varianti devono essere coperte.

enum Moneta {
    Centesimo,
    DueCentesimi,
    CinqueCentesimi,
    DieciCentesimi,
    VentiCentesimi,
    CinquantaCentesimi,
    Euro,
    DueEuro,
}

fn valore_in_centesimi(moneta: &Moneta) -> u32 {
    match moneta {
        Moneta::Centesimo => 1,
        Moneta::DueCentesimi => 2,
        Moneta::CinqueCentesimi => 5,
        Moneta::DieciCentesimi => 10,
        Moneta::VentiCentesimi => 20,
        Moneta::CinquantaCentesimi => 50,
        Moneta::Euro => 100,
        Moneta::DueEuro => 200,
    }
}

fn main() {
    let moneta = Moneta::CinquantaCentesimi;
    println!("Valore: {} centesimi", valore_in_centesimi(&moneta));
}

Per gestire varianti non specifiche, si usa il pattern _ o una variabile catch-all:

fn descrivi_moneta(moneta: &Moneta) -> &str {
    match moneta {
        Moneta::Euro | Moneta::DueEuro => "moneta grande",
        _ => "moneta piccola",
    }
}

Metodi sugli Enum

Come per le struct, anche gli enum possono avere metodi definiti in blocchi impl:

#[derive(Debug)]
enum Forma {
    Cerchio(f64),
    Rettangolo(f64, f64),
    Triangolo(f64, f64),
}

impl Forma {
    fn area(&self) -> f64 {
        match self {
            Forma::Cerchio(raggio) => std::f64::consts::PI * raggio * raggio,
            Forma::Rettangolo(base, altezza) => base * altezza,
            Forma::Triangolo(base, altezza) => base * altezza / 2.0,
        }
    }

    fn descrizione(&self) -> String {
        match self {
            Forma::Cerchio(r) => format!("Cerchio con raggio {}", r),
            Forma::Rettangolo(b, h) => format!("Rettangolo {}x{}", b, h),
            Forma::Triangolo(b, h) => format!("Triangolo base {} altezza {}", b, h),
        }
    }

    fn e_cerchio(&self) -> bool {
        matches!(self, Forma::Cerchio(_))
    }
}

fn main() {
    let forme: Vec<Forma> = vec![
        Forma::Cerchio(5.0),
        Forma::Rettangolo(4.0, 6.0),
        Forma::Triangolo(3.0, 8.0),
    ];

    for forma in &forme {
        println!("{}: area = {:.2}", forma.descrizione(), forma.area());
    }

    let cerchi: Vec<&Forma> = forme.iter().filter(|f| f.e_cerchio()).collect();
    println!("Numero di cerchi: {}", cerchi.len());
}

Enum della Libreria Standard

Rust include diversi enum fondamentali nella libreria standard:

fn main() {
    // Option<T>: rappresenta un valore che puo essere presente o assente
    let numero: Option<i32> = Some(42);
    let niente: Option<i32> = None;

    // Result<T, E>: rappresenta un'operazione che puo riuscire o fallire
    let ok: Result<i32, String> = Ok(100);
    let errore: Result<i32, String> = Err(String::from("errore"));

    // Ordering: usato per i confronti
    use std::cmp::Ordering;
    let confronto: Ordering = 5.cmp(&3);
    match confronto {
        Ordering::Less => println!("Minore"),
        Ordering::Equal => println!("Uguale"),
        Ordering::Greater => println!("Maggiore"),
    }
}

Enum con Valori Discriminanti

Come in C, si possono assegnare valori interi alle varianti:

#[derive(Debug)]
enum CodiceHTTP {
    Ok = 200,
    NonTrovato = 404,
    ErroreServer = 500,
}

fn main() {
    println!("OK: {}", CodiceHTTP::Ok as i32);           // 200
    println!("Non trovato: {}", CodiceHTTP::NonTrovato as i32); // 404
}

Conclusione

Gli enum in Rust vanno ben oltre le semplici enumerazioni di altri linguaggi. La possibilita di associare dati a ciascuna variante, combinata con il pattern matching esaustivo, permette di modellare stati e situazioni complesse in modo sicuro e leggibile. Gli enum sono alla base di molti pattern idiomatici di Rust, da Option e Result fino alle macchine a stati e ai comandi di un’applicazione.