In Go è adottato un approccio pragmatico alla gestione delle risorse. Invece di try-finally, comune in altri linguaggi, qui c'è defer: un meccanismo integrato che registra una funzione "rimandata" e la esegue al termine del contesto. Questo strumento è spesso utilizzato per gestire automaticamente il rilascio delle risorse (file, connessioni di rete).
Se si dimentica di chiamare Close su un file o una connessione, potrebbe verificarsi una perdita di risorse o un blocco — cosa critica nelle applicazioni server e di file. La chiamata rimandata con defer garantisce l'esecuzione della funzione di conclusione anche in caso di errore o panic. Tuttavia, ci sono casi particolari in cui un uso errato di defer porta a problemi: invocare defer in un ciclo, passare un oggetto non valido o lavorare con un gran numero di defer può causare overhead.
Chiamate sempre defer f.Close() subito dopo aver aperto con successo una risorsa, per evitare chiusure dimenticate. Non utilizzare defer in cicli stretti per aumentare la velocità e risparmiare memoria, se si aprono molti file. Cerca di incapsulare l'apertura di file/risorse in una funzione, per minimizzare il contesto.
Esempio di codice:
file, err := os.Open("data.txt") if err != nil { log.Fatal(err) } defer file.Close() // chiusura garantita // ... lavorare con il file
Caratteristiche chiave:
Quando viene eseguito defer e vengono calcolati i suoi parametri?
I parametri della funzione in defer vengono calcolati immediatamente al momento della dichiarazione di defer, e non al momento della sua esecuzione.
Esempio di codice:
func main() { a := 1 defer fmt.Println(a) // ricorderà 1 a = 42 } // stamperà 1
Può defer causare perdite di memoria o rallentamenti?
Sì, se si utilizza defer in un ciclo dove vengono aperti molti file o oggetti, ogni defer viene registrato nello stack delle chiamate rimandate, che viene pulito solo al termine della funzione, portando a una crescita inutile della memoria.
Esempio di codice:
for i := 0; i < 10000; i++ { f, _ := os.Open("file.txt") defer f.Close() // tiene tutti e 10000 file aperti fino alla fine di main }
Cosa succede se la chiamata a f.Close() restituisce un errore e questa viene "inghiottita"?
La prassi standard è loggare l'errore della chiusura delle risorse. Ignorare questo punto potrebbe portare a non notare errori o salvataggi parziali dei file, ad esempio quando un file temporaneo non viene eliminato o durante fallimenti di rete.
In un ciclo di elaborazione dei file, lo sviluppatore posiziona defer f.Close() per ogni file. Di conseguenza, vengono aperti decine di migliaia di file, rallentando l'esecuzione del programma e terminando i file descriptor nel sistema.
Pro:
Contro:
Nel ciclo, l'elaborazione di ogni file viene eseguita in una funzione separata, dove defer f.Close() è solo uno per elaborazione e libera immediatamente la risorsa.
Pro:
Contro: