cat swift/optionals-modello-di-assenza.md
Optionals come modello di assenza
Gli optionals non sono una seccatura sintattica: sono il modo con cui Swift rende visibile la possibilità che un dato manchi.
Gli optionals sono uno dei primi punti in cui Swift sembra più severo di altri linguaggi. In realtà sono uno dei suoi strumenti più importanti.
Un optional non significa “qui c’è un problema”. Significa: questo dato può essere assente, e il codice deve tenerne conto.
let email: String? = nil
Il tipo String? dice qualcosa che una semplice String non dice: l’email potrebbe non esserci.
Assenza come informazione
In un dominio reale, l’assenza può avere significati diversi.
Un utente potrebbe non avere ancora verificato l’email. Un prodotto potrebbe non avere uno sconto. Un ordine potrebbe non avere ancora un codice di tracciamento.
struct Utente {
let nome: String
let emailVerificata: String?
}
struct Prodotto {
let nome: String
let scontoPercentuale: Int?
}
struct Ordine {
let codice: String
let trackingCode: String?
}
In tutti questi casi nil non è un errore tecnico. È uno stato possibile del dominio.
Optional non è una stringa vuota
Un errore molto comune è usare valori finti per evitare gli optionals:
let trackingCode = ""
Ma una stringa vuota può significare molte cose:
- non è ancora disponibile;
- è stato cancellato;
- è arrivato vuoto da una API;
- qualcuno ha dimenticato di valorizzarlo.
Con un optional il significato è più chiaro:
let trackingCode: String? = nil
Assente significa assente.
Unwrapping con if let
Per usare il valore dentro un optional devi prima gestire il caso in cui manchi.
let trackingCode: String? = "IT123"
if let codice = trackingCode {
print("Traccia ordine: \(codice)")
} else {
print("Tracking non ancora disponibile")
}
if let crea una costante non optional solo nel ramo in cui il valore esiste.
guard let: uscire presto
Quando l’assenza impedisce di continuare, guard let rende il flusso più leggibile.
func inviaEmailConferma(a email: String?) {
guard let email else {
print("Impossibile inviare: email mancante")
return
}
print("Invio conferma a \(email)")
}
Dopo il guard, email è una String, non più una String?.
Questa forma è molto usata per validare dati in ingresso.
Optional chaining
Quando vuoi accedere a proprietà o metodi solo se il valore esiste, puoi usare ?..
struct Profilo {
let bio: String?
}
struct Utente {
let profilo: Profilo?
}
let utente = Utente(profilo: Profilo(bio: "Sviluppatrice Swift"))
let lunghezzaBio = utente.profilo?.bio?.count
Il tipo di lunghezzaBio è Int?, perché ogni passaggio potrebbe fallire.
Nil coalescing
Quando hai un valore di fallback sensato, usa ??.
let nomeVisualizzato = utente.profilo?.bio ?? "Nessuna bio disponibile"
Attenzione: il fallback deve essere davvero corretto per il dominio. Non usarlo per nascondere un caso che invece andrebbe gestito esplicitamente.
Force unwrap: quasi sempre un odore
Swift permette di forzare l’estrazione:
let email: String? = nil
print(email!)
Se email è nil, il programma va in crash.
Il force unwrap ! non è vietato in assoluto, ma dovrebbe essere raro. Se lo usi spesso, probabilmente stai dicendo al compilatore: “fidati di me”, proprio nel punto in cui Swift ti stava chiedendo di essere preciso.
Optional o enum?
Un optional è perfetto quando ci sono due stati:
- c’è un valore;
- non c’è un valore.
Ma se gli stati sono più ricchi, un enum è spesso migliore.
Versione troppo povera:
let pagamentoCompletato: Bool?
Cosa significa nil? Non iniziato? In attesa? Errore?
Meglio:
enum StatoPagamento {
case nonIniziato
case inAttesa
case completato
case fallito(motivo: String)
}
Gli optionals non devono diventare un posto dove nascondere stati che meritano un nome.
Esempio nel carrello
struct Coupon {
let codice: String
let scontoPercentuale: Int
}
struct Carrello {
let prodotti: [Prodotto]
let coupon: Coupon?
var descrizioneCoupon: String {
guard let coupon else {
return "Nessun coupon applicato"
}
return "Coupon \(coupon.codice): -\(coupon.scontoPercentuale)%"
}
}
Qui coupon è optional perché un carrello può legittimamente non avere un coupon.
Checklist
Usa un optional quando:
- l’assenza è uno stato valido;
- ci sono solo due possibilità: valore presente o valore assente;
- il chiamante deve essere costretto a gestire l’assenza.
Evita un optional quando:
- stai nascondendo più stati diversi dentro
nil; - hai un valore di default che è sempre corretto;
- l’assenza dovrebbe essere un errore;
- stai usando
!per aggirare il compilatore.
Esercizio
Estendi il dominio carrello con:
- un
Utentecon email opzionale; - un
Couponopzionale nel carrello; - un
Ordinecon tracking opzionale.
Poi scrivi tre funzioni:
func descriviEmail(_ utente: Utente) -> String
func descriviCoupon(_ carrello: Carrello) -> String
func descriviTracking(_ ordine: Ordine) -> String
Ogni funzione deve gestire esplicitamente il caso di assenza senza usare !.