Go ha due modi per dichiarare una variabile: var, che funziona ovunque, e :=, una forma breve che funziona dentro le funzioni e deduce il tipo.
Ogni variabile ha un tipo fisso deciso in fase di compilazione. Quando fornisci un valore, Go ne deduce il tipo, quindi scrivere anche il tipo (var city string = "Lisbon") è valido ma ridondante.
var e :=
var | := | |
|---|---|---|
| Dove | a livello di pacchetto e dentro le funzioni | solo dentro le funzioni |
| Tipo | facoltativo; obbligatorio quando non c'è un valore | sempre dedotto |
| Valore iniziale | facoltativo (altrimenti valore zero) | obbligatorio |
| Ridichiarazione | mai | ammessa se almeno una variabile a sinistra è nuova |
La scelta idiomatica dentro una funzione:
x := valuequando hai un valore di partenza.var x Tquando vuoi il valore zero, che segnala "vuoto di proposito":var buf bytes.Buffer,var total int.var x T = valuequando il tipo dedotto non è quello che vuoi, per esempiovar ratio float64 = 1(un sempliceratio := 1sarebbe unint).
:= fuori da una funzione è un errore di sintassi:
./main.go:5:1: syntax error: non-declaration statement outside function body
Valori zero
Una variabile dichiarata senza un valore non contiene mai dati casuali. Contiene il valore zero del suo tipo:
| Tipo | Valore zero |
|---|---|
| interi, float | 0 |
bool | false |
string | "" |
| puntatori, slice, mappe, canali, funzioni, interfacce | nil |
| array, struct | ogni elemento o campo impostato al suo valore zero |
Le buone API Go sono progettate in modo che il valore zero sia utile: un sync.Mutex a zero è sbloccato, un bytes.Buffer a zero è vuoto e pronto, e len, range e append funzionano tutti su uno slice nil. Quella a cui fare attenzione è la mappa: leggere da una mappa nil restituisce valori zero, ma scriverci va in panic. Crea le mappe con make o con un letterale prima di memorizzarci qualcosa.
Dichiarare più variabili
Il blocco var ( ... ) raggruppa variabili a livello di pacchetto correlate tra loro. In un'assegnazione multipla come x, y = y, x, ogni espressione a destra viene valutata prima di qualsiasi assegnazione, ed è per questo che lo scambio funziona.
:= con più nomi non ridichiara nulla finché almeno uno dei nomi a sinistra è nuovo. I nomi già esistenti nello stesso scope vengono semplicemente assegnati. È questo che ti permette di riusare err:
f, err := os.Open("a.txt") // declares f and err
g, err := os.Open("b.txt") // declares g, assigns to the existing err
Se nessuno dei nomi è nuovo, è un errore:
./main.go:7:4: no new variables on left side of :=
Usa un semplice = per assegnare un valore a una variabile che esiste già.
Le variabili inutilizzate sono errori
Una variabile locale dichiarata ma mai letta blocca la build:
./main.go:9:2: declared and not used: x
Non è un avviso che puoi disattivare. Elimina la variabile, usala oppure, durante il debug, zittiscila con l'identificatore vuoto: _ = x. L'identificatore vuoto scarta anche i valori restituiti che non ti servono: _, err := fmt.Println("hi").
Le variabili a livello di pacchetto e i parametri delle funzioni sono esenti da questa regola.
Scope e shadowing
Una variabile esiste dalla sua dichiarazione fino alla fine del blocco che la contiene ({ ... }). Un := in un blocco interno crea una nuova variabile, anche se fuori ne esiste una con lo stesso nome. Quella esterna viene nascosta, non modificata:
Il programma stampa inside: 42 e poi outside: 0. Il := dentro l'if ha dichiarato sia count sia err come nuove variabili nel blocco interno, perché nessuna delle due esisteva ancora lì. Compila senza problemi ed è una fonte classica di bug del tipo "il mio valore è sparito".
La soluzione è dichiarare prima err e assegnare con =:
var err error
count, err = strconv.Atoi(input)
go vet non segnala lo shadowing per default. Lo fa l'analizzatore separato shadow, e linter come golangci-lint possono attivarlo.
Nomi
- Usa il
camelCase:maxRetries,userID. Nonmax_retries. - Tieni gli acronimi tutti maiuscoli o tutti minuscoli:
userID,httpClient,parseURL, nonuserId. - Nomi brevi per scope brevi:
iin un ciclo,rper un reader,errper un errore. Nomi più lunghi e descrittivi per le variabili a livello di pacchetto. - Un nome con l'iniziale maiuscola a livello di pacchetto (
var Timeout) viene esportato agli altri pacchetti. Vedi pacchetti e import.
Variabili a livello di pacchetto
Le variabili dichiarate fuori dalle funzioni vivono per tutta la durata del programma e vengono inizializzate prima che parta main, nell'ordine delle dipendenze. Sono comode per la configurazione e le cache, e sono condivise da tutte le goroutine. Se più di una goroutine ci scrive, proteggile con un mutex o usa sync/atomic, altrimenti hai una data race.
Preferisci le costanti per i valori che non cambiano mai. Una const non può essere modificata per sbaglio e non costa nulla durante l'esecuzione.
Domande frequenti
Qual è la differenza tra var e := in Go?
:= dichiara e inizializza una variabile con un tipo dedotto, e funziona solo dentro le funzioni: count := 10. var funziona ovunque (anche a livello di pacchetto), ti permette di indicare il tipo e può omettere il valore iniziale per ottenere il valore zero: var count int. Dentro le funzioni, il Go idiomatico usa := quando c'è un valore iniziale e var quando vuoi il valore zero.
Cosa sono i valori zero in Go?
Ogni variabile dichiarata senza un valore riceve il valore zero del suo tipo: 0 per i numeri, false per bool, "" per string e nil per puntatori, slice, mappe, canali, funzioni e interfacce. Le struct ricevono il valore zero di ogni campo. In Go non esistono variabili non inizializzate.
Come risolvo "declared and not used" in Go?
Go rifiuta una variabile locale che viene assegnata ma mai letta. Usala, eliminala oppure, durante il debug, assegnala all'identificatore vuoto con _ = x. Le variabili a livello di pacchetto e i parametri delle funzioni sono esenti.
Come dichiaro una variabile globale in Go?
Usa var a livello di pacchetto, fuori da qualsiasi funzione: var counter int. La forma breve := lì non è ammessa. Un nome con l'iniziale maiuscola (var Counter int) è visibile agli altri pacchetti; uno minuscolo è visibile solo nel proprio pacchetto. Proteggi le variabili a livello di pacchetto con un mutex se ci scrivono delle goroutine.