Что такое замыкание в Go?
Что проверяют
Понимаете ли вы, что замыкание в Go захватывает саму переменную, а не её значение, и к чему это приводит в горутинах и циклах. Следом почти наверняка спросят про баг с переменной цикла и Go 1.22.
Как отвечать
Замыкание это функциональный литерал, который ссылается на переменные из окружающей области видимости. Функции в Go значения первого класса.
Как это устроено:
- захват по ссылке: замыкание работает с той же переменной, что и внешний код. Изменение с любой стороны видно обеим, копии не делается;
- escape analysis: если замыкание переживает функцию, где создано, компилятор переносит захваченные переменные в кучу. Видно через
go build -gcflags=-m; - состояние между вызовами: в примере со счётчиком
countживёт после возврата изcounter(), потому что на него ссылается возвращённая функция.
Где это встречается в реальном коде:
- middleware в
net/http:func(next http.Handler) http.Handlerвозвращаетhttp.HandlerFunc, замкнутый наnextи на зависимости вроде логгера; - functional options:
func WithTimeout(d time.Duration) Optionвозвращает функцию, которая настраивает структуру; deferсrecover()и компараторы дляsort.Sliceиslices.SortFunc;- итераторы с Go 1.23:
iter.Seq[T]это функция, принимающаяyield, иfor rangeпо ней держится на замыканиях.
Про циклы: до Go 1.22 переменная цикла была одна на все итерации, и go func() { fmt.Println(v) }() внутри for часто печатал последнее значение. С 1.22 каждая итерация получает свою переменную, но только в модулях, где в go.mod указано go 1.22 или новее.
Где спотыкаются
Меняют захваченную переменную из нескольких горутин без синхронизации. Это гонка данных, её ловит go test -race, а исправляют мьютексом, каналом или sync/atomic.
Путают defer fmt.Println(i) и defer func() { fmt.Println(i) }(). В первом случае аргумент вычисляется сразу, во втором значение читается в момент выполнения defer.
Говорят, что баг с переменной цикла актуален везде, или что его больше не существует. Правильно: зависит от версии в go.mod, а в старом коде ещё встречается v := v.