Shadowing (затенение) — это когда переменная во внутренней области видимости скрывает ("затеняет") переменную с таким же именем из внешней области видимости. В Go это возможно из-за особенностей блока объявления переменных :=, особенно внутри блоков (if, for, switch и т.д.).
x := 5 if true { x := 10 // Эта x — новая переменная, действует только в этом if fmt.Println(x) // 10 } fmt.Println(x) // 5, а не 10
Иногда это бывает полезно, но часто приводит к ошибкам, если забыть, что заново объявленная переменная — это не та же переменная, что наружная.
Вопрос: "Что выведет следующий код?"
x := 7 if true { x, y := 1, 2 fmt.Println(x, y) } fmt.Println(x)
Ответ:
if будут объявлены новые переменные x и y, они доступны только в блоке if.x — это всё та же внешняя переменная, её значение не изменится.Вывод:
1 2
7
История
Утечка ресурсов при shadowing err: Типичная ошибка — затенение переменной ошибки при работе с файлами.
f, err := os.Open("file.txt") if err != nil { return err } if err := f.Close(); err != nil { return err } // это новая переменная err!
Оператор := создаёт новую переменную err только внутри блока, а внешняя переменная err не меняется. Если обработка ошибок ожидается во внешней переменной, это приводит к потере информации об ошибках.
История
Shadowing структуры — невидимые баги: Внутри вложенного блока разработчик переопределил структуру с тем же именем. В результате часть логики работала с внутренней версией, а другая — с внешней, что привело к странным багам и потере данных в сервисе биллинга.
История
Shadowing в циклах ломает подсчёты:
В автоматизированной системе обработки заявок в цикле использовали := вместо = для увеличения счетчика, из-за чего внутри цикла создавалась новая переменная и увеличивалась, а внешний счётчик оставался неизменным. Система недосчитывала заявки и занижала важную статистику.