Rust offre tipi avanzati che permettono di modellare dati complessi in modo elegante e sicuro. Le struct sono perfette per raggruppare dati correlati, mentre gli enum consentono di rappresentare varianti e stati. Con pattern matching, Rust fornisce un potente strumento per gestire questi tipi in modo chiaro ed espressivo. Infine, tipi speciali come Option e Result aiutano a gestire casi di valore mancante ed errori in modo sicuro.
In questa lezione, esploreremo questi concetti con esempi pratici.
Strutture (Struct) e Utilizzo
Le struct permettono di raggruppare più valori sotto un unico tipo. Ci sono tre tipi principali di struct in Rust:
- Struct Classica
- Struct a Tuple
- Struct Unitaria
Esempio: Struct Classica
Una struct classica ha campi nominati:
struct Persona {
nome: String,
eta: u32,
}
fn main() {
let persona = Persona {
nome: String::from("Alice"),
eta: 30,
};
println!("Nome: {}, Età: {}", persona.nome, persona.eta);
}
Esempio: Struct a Tuple
Le struct a tuple non hanno nomi di campo, ma utilizzano posizioni:
struct Punto(i32, i32);
fn main() {
let p = Punto(10, 20);
println!("Punto: ({}, {})", p.0, p.1);
}
Struct Unitaria
Le struct unitarie non hanno campi. Vengono spesso utilizzate per implementare trait.
struct Unitaria;
fn main() {
let _u = Unitaria;
}
Enum per Rappresentare Stati e Varianti
Gli enum in Rust sono ideali per rappresentare un insieme di varianti. Ogni variante può contenere dati aggiuntivi.
Esempio: Enum di Base
enum Stagione {
Primavera,
Estate,
Autunno,
Inverno,
}
fn main() {
let stagione_corrente = Stagione::Estate;
match stagione_corrente {
Stagione::Primavera => println!("È primavera!"),
Stagione::Estate => println!("È estate!"),
Stagione::Autunno => println!("È autunno!"),
Stagione::Inverno => println!("È inverno!"),
}
}
Enum con Dati Aggiuntivi
Ogni variante può contenere dati:
enum Messaggio {
Testo(String),
Coordinata { x: i32, y: i32 },
Numero(i32),
}
fn main() {
let messaggio = Messaggio::Coordinata { x: 10, y: 20 };
match messaggio {
Messaggio::Testo(testo) => println!("Messaggio: {}", testo),
Messaggio::Coordinata { x, y } => println!("Coordinate: ({}, {})", x, y),
Messaggio::Numero(num) => println!("Numero: {}", num),
}
}
Pattern Matching con match
Il pattern matching in Rust consente di verificare e destrutturare valori complessi in modo conciso e leggibile.
Esempio: Pattern Matching con Enum
enum Stato {
Online,
Offline,
InRiparazione,
}
fn stato_dispositivo(stato: Stato) {
match stato {
Stato::Online => println!("Il dispositivo è online."),
Stato::Offline => println!("Il dispositivo è offline."),
Stato::InRiparazione => println!("Il dispositivo è in riparazione."),
}
}
fn main() {
let stato = Stato::Online;
stato_dispositivo(stato);
}
Uso di _ per Gestire Casi Non Specificati
fn valuta(numero: i32) {
match numero {
1 => println!("Uno"),
2 => println!("Due"),
_ => println!("Altro numero"),
}
}
fn main() {
valuta(5);
}
Uso di Option e Result
Rust non utilizza null, ma fornisce i tipi Option e Result per rappresentare casi di valore mancante ed errori.
Option per Valori Mancanti
Il tipo Option può essere Some(T) per un valore presente o None per un valore mancante.
fn trova_valore(numero: i32) -> Option {
if numero > 0 {
Some(numero * 2)
} else {
None
}
}
fn main() {
match trova_valore(5) {
Some(valore) => println!("Valore trovato: {}", valore),
None => println!("Nessun valore trovato."),
}
}
Result per Gestire Errori
Result è utilizzato per operazioni che possono fallire. Può essere Ok(T) per un risultato valido o Err(E) per un errore.
fn dividi(a: i32, b: i32) -> Result {
if b == 0 {
Err(String::from("Errore: divisione per zero"))
} else {
Ok(a / b)
}
}
fn main() {
match dividi(10, 0) {
Ok(risultato) => println!("Risultato: {}", risultato),
Err(errore) => println!("{}", errore),
}
}
Esercizi Pratici
- Esercizio 1: Crea una struct
Rettangolocon campialtezzaelarghezza, e implementa una funzione per calcolare l’area. - Esercizio 2: Definisci un enum
Operazionecon varianti per addizione, sottrazione e moltiplicazione, e utilizzamatchper eseguire l'operazione corrispondente. - Esercizio 3: Usa
Optionper scrivere una funzione che restituisce il quadrato di un numero positivo oNonese il numero è negativo. - Esercizio 4: Scrivi una funzione che utilizza
Resultper leggere un file e gestire possibili errori.
Conclusione
Le struct e gli enum in Rust ti permettono di modellare i dati in modo preciso ed espressivo, mentre pattern matching e tipi come Option e Result garantiscono un controllo efficace sui flussi di dati e sugli errori. Questi strumenti, insieme, ti aiuteranno a scrivere codice robusto, sicuro e facilmente manutenibile. Nel prossimo articolo esploreremo argomenti come Vettori, Array e Iteratori. Continua a esercitarti con gli esempi forniti e padroneggia questi concetti avanzati di Rust!
Lascia un commento