In Go, le regole di visibilità delle variabili (scoping) sono rigidamente definite dai blocchi ({}), e il nome di una variabile può essere oscurato (shadowing) all'interno di ambiti annidati. Si presentano molte insidie specialmente nelle funzioni annidate, nelle funzioni anonime, nei cicli e quando si dichiarano variabili con lo stesso nome a diversi livelli.
Go ha minimizzato appositamente il comportamento "magico" della visibilità per rendere il codice più leggibile. Tuttavia, la flessibilità della sintassi e la possibilità di dichiarare nuovamente variabili tramite la forma abbreviata := possono portare a errori di comprensione.
Se si dichiara una variabile con lo stesso nome in una funzione annidata o in un blocco di ciclo, la variabile esterna non sarà accessibile (oscurata — shadowed). Nella maggior parte dei casi, ciò non viene notato dal compilatore e può facilmente causare errori, specialmente quando si lavora con closure. Un altro errore comune è dichiarare una nuova variabile in un blocco if o in for-init e poi tentare di accedervi al di fuori di quel blocco.
Controlla sempre i livelli di visibilità. Non utilizzare nomi di variabili identici in blocchi annidati o funzioni anonime senza una reale necessità, evita nomi brevi e fai attenzione all'uso di :=.
Esempio di codice:
package main import "fmt" func main() { x := 1 { x := 2 // oscura x da main() fmt.Println("Inner x:", x) } fmt.Println("Outer x:", x) for i := 0; i < 3; i++ { x := i // viene creata una nuova x ad ogni iterazione go func() { fmt.Println("Goroutine x:", x) }() } }
In questo esempio, la variabile esterna x non viene modificata, e una nuova x viene creata all'interno del blocco. Nel secondo ciclo, la variabile x viene catturata nella funzione annidata — il risultato può essere inaspettato.
Caratteristiche principali:
:= all'interno di un blocco crea sempre una nuova variabile, anche se ce n'è già una esterna.1. Quale valore della variabile verrà stampato per ultimo nel blocco annidato in caso di oscuramento?
Il valore della variabile esterna, poiché la variabile interna esiste solo nel blocco.
2. Cosa succede se si tenta di accedere a una variabile dichiarata all'interno di un blocco if/for al di fuori di quel blocco?
Il compilatore restituirà un errore: la variabile è fuori dall'ambito.
if true { y := 5 } fmt.Println(y) // errore
3. Come evitare un valore inaspettato durante la creazione di goroutine in un ciclo su una variabile?
Passando la variabile come parametro alla funzione:
for i := 0; i < 3; i++ { go func(val int) { fmt.Println(val) }(i) }
:= modifichi una variabile già esistente (crea una nuova variabile).Il ciclo inizializza diverse goroutine per l'elaborazione parallela, ma all'interno della closure viene utilizzata la variabile di ciclo senza passaggio — tutte le goroutine lavorano con il suo "ultimo" valore.
Pro:
Contro:
Passare la variabile di ciclo come parametro alla closure — ogni goroutine riceve il proprio valore.
Pro:
Contro: