cat swift/mutabilita-immutabilita-copia.md
Mutabilità, immutabilità e copia
Come Swift usa let, var e semantica di copia per rendere più leggibile il cambiamento.
Swift prende molto sul serio una domanda semplice: questo valore deve cambiare?
La risposta è visibile nella sintassi:
let nome = "Ada"
var tentativi = 0
let dichiara una costante. var dichiara una variabile. La differenza non è cosmetica: comunica al compilatore e a chi legge il codice se quel valore fa parte di uno stato stabile o di uno stato in evoluzione.
La mutabilità come informazione
In un programma reale, troppa mutabilità rende difficile capire cosa può cambiare e quando.
Questo codice è tecnicamente semplice:
var totale = 0
totale = totale + 1000
totale = totale + 2500
totale = totale - 500
Ma se il calcolo cresce, il valore di totale diventa una storia da ricostruire passo dopo passo.
A volte è più chiaro descrivere il risultato come trasformazione:
let prezzi = [1000, 2500]
let sconto = 500
let totale = prezzi.reduce(0, +) - sconto
Non sempre questa versione è migliore, ma mostra un principio: meno stati intermedi mutabili, meno superfici per errori.
let non significa “immutabilità profonda” per le classi
Con le struct, let rende immutabile il valore.
struct Prodotto {
var nome: String
}
let prodotto = Prodotto(nome: "Libro")
// prodotto.nome = "Quaderno" // errore
Con le class, invece, let rende costante il riferimento, non necessariamente l’oggetto.
final class Sessione {
var token: String
init(token: String) {
self.token = token
}
}
let sessione = Sessione(token: "abc")
sessione.token = "xyz" // consentito
Questa distinzione è fondamentale. let sessione significa: la variabile sessione punterà sempre allo stesso oggetto. Non significa: l’oggetto non può cambiare.
Copiare non è sempre duplicare subito
Swift usa spesso ottimizzazioni interne come il copy-on-write. Per molti tipi standard, come Array, copiare un valore non significa necessariamente duplicare subito tutta la memoria.
var a = [1, 2, 3]
var b = a
b.append(4)
Mentalmente puoi ragionare come se b fosse una copia indipendente. Swift e le sue librerie si occupano di evitare copie costose finché possibile.
Questo è un punto bello del linguaggio: ti offre una semantica semplice, mentre sotto può ottimizzare.
Mutating: cambiare se stessi
Le struct sono value types. Quando un metodo cambia una proprietà della struct, deve dichiararlo:
struct Carrello {
private(set) var prodotti: [String] = []
mutating func aggiungi(_ prodotto: String) {
prodotti.append(prodotto)
}
}
mutating è una promessa esplicita: questo metodo modifica il valore.
Da fuori:
var carrello = Carrello()
carrello.aggiungi("Libro")
Se carrello fosse dichiarato con let, il metodo non potrebbe essere chiamato:
let carrelloBloccato = Carrello()
// carrelloBloccato.aggiungi("Libro") // errore
Questo rende evidente dove il programma consente cambiamenti.
Disegnare API con cambiamento esplicito
Hai due modi principali per modellare una trasformazione.
Il primo modifica il valore:
struct Carrello {
var righe: [String]
mutating func aggiungi(_ prodotto: String) {
righe.append(prodotto)
}
}
Il secondo restituisce un nuovo valore:
struct Carrello {
let righe: [String]
func aggiungendo(_ prodotto: String) -> Carrello {
Carrello(righe: righe + [prodotto])
}
}
Non c’è una risposta universale. La prima forma è comoda quando lavori localmente con uno stato. La seconda è utile quando vuoi trasformazioni più prevedibili, testabili e facilmente componibili.
Esempio: carrello immutabile
struct Prodotto {
let nome: String
let prezzoInCent: Int
}
struct Carrello {
let prodotti: [Prodotto]
var totaleInCent: Int {
prodotti.reduce(0) { totale, prodotto in
totale + prodotto.prezzoInCent
}
}
func aggiungendo(_ prodotto: Prodotto) -> Carrello {
Carrello(prodotti: prodotti + [prodotto])
}
}
let vuoto = Carrello(prodotti: [])
let conLibro = vuoto.aggiungendo(Prodotto(nome: "Libro", prezzoInCent: 1900))
print(vuoto.totaleInCent) // 0
print(conLibro.totaleInCent) // 1900
Il valore originale resta intatto. Questo stile è spesso utile nei test e nella logica di dominio.
Errore comune
Un errore frequente è dichiarare tutto var “per comodità”.
var nome = "Libro"
var prezzoInCent = 1900
var categoria = "libri"
Se qualcosa non deve cambiare, dichiaralo let. Non è solo una protezione tecnica: è documentazione eseguibile.
Checklist
Quando scrivi una proprietà o una variabile, chiediti:
- questo valore deve cambiare dopo l’inizializzazione?
- chi è autorizzato a cambiarlo?
- il cambiamento è locale oppure visibile altrove?
- una funzione che restituisce un nuovo valore sarebbe più chiara?
- sto usando
varsolo per abitudine?
Esercizio
Riscrivi Carrello in due versioni:
- versione mutabile con
mutating func aggiungi; - versione immutabile con
func aggiungendo.
Poi scrivi tre test manuali con print:
- il carrello vuoto resta vuoto;
- il carrello modificato contiene il prodotto;
- il totale viene calcolato correttamente.