W Go zasady zakresu widoczności zmiennych (scoping) są ściśle określane przez bloki ({}), a nazwa zmiennej może być zacieniana (shadowing) w zagnieżdżonych obszarach. Szczególnie wiele pułapek pojawia się w zagnieżdżonych funkcjach, funkcjach anonimowych, pętlach oraz przy deklaracji zmiennych o tej samej nazwie na różnych poziomach.
Go celowo zminimalizował "magiczne" zachowanie związane z zakresem widoczności, aby uczynić kod bardziej czytelnym. Jednak elastyczność składni oraz dopuszczająca ponowną deklarację zmiennych w krótkiej formie := prowadzi do błędów w percepcji.
Jeśli w zagnieżdżonej funkcji lub w bloku pętli zadeklarujemy zmienną o tej samej nazwie co na poziomie wyższym, zewnętrzna zmienna będzie niedostępna (zacieniona — shadowed). W większości przypadków to nie jest zauważane przez kompilator i łatwo staje się przyczyną błędów, szczególnie w przypadku pracy z closure. Innym powszechnym błędem jest deklarowanie nowej zmiennej w bloku if lub w for-init, a następnie próba odwołania się do niej poza blokiem.
Zawsze zwracaj uwagę na poziomy zakresu widoczności. Nie używaj tych samych nazw zmiennych w zagnieżdżonych blokach lub funkcjach anonimowych bez rzeczywistej potrzeby, unikaj krótkich nazw i bądź ostrożny przy użyciu :=.
Przykład kodu:
package main import "fmt" func main() { x := 1 { x := 2 // zacienia x z main() fmt.Println("Inner x:", x) } fmt.Println("Outer x:", x) for i := 0; i < 3; i++ { x := i // nowy x tworzony przy każdej iteracji go func() { fmt.Println("Goroutine x:", x) }() } }
W tym przykładzie zewnętrzna zmienna x nie jest zmieniana, podczas gdy nowe x jest tworzone wewnątrz bloku. W drugiej pętli zmienna x jest chwytana w zagnieżdżonej funkcji — rezultaty mogą być nieoczekiwane.
Kluczowe cechy:
:= wewnątrz bloku zawsze tworzy nową zmienną, nawet jeśli zewnętrzna już istnieje.1. Jaka wartość zmiennej zostanie wydrukowana ostatnia w zagnieżdżonym bloku przy zacienieniu?
Wartość zewnętrznej zmiennej, ponieważ wewnętrzna zmienna istnieje tylko w bloku.
2. Co się stanie, jeśli spróbujesz odwołać się do zmiennej zadeklarowanej wewnątrz bloku if/for, poza tym blokiem?
Kompilator zgłosi błąd: zmienna poza zakresem widoczności.
if true { y := 5 } fmt.Println(y) // błąd
3. Jak uniknąć nieoczekiwanej wartości przy tworzeniu goroutine w pętli z zmienną?
Przekazać zmienną jako parametr funkcji:
for i := 0; i < 3; i++ { go func(val int) { fmt.Println(val) }(i) }
:= zmieni już istniejącą zmienną (tworzy nową).Pętla inicjalizuje kilka goroutine do równoległego przetwarzania, ale wewnątrz closure używana jest zmienna pętli bez przekazywania — wszystkie goroutines działają z jej "ostatnią" wartością.
Zalety:
Wady:
Przekazywanie zmiennej pętli jako parametru closure — każda goroutine otrzymuje swoją wartość.
Zalety:
Wady: