TGViewer
Go Update Go Update @go_update · 3.22K subscribers
Post #47 3.96K
🆒 Fixing For Loops in Go 1.22

Рассказывая про счастливое далекое будущее, я часто забываю рассказать про хорошее (почти) настоящее. Но перед тем как начать, я задам вопрос: а что выведет следующая программа?


package main

import "fmt"

func main() {
var slc []fmt.Stringer

for _, s := range []st{{"Hello "}, {"World"}, {"!"}} {
slc = append(slc, s.toStringer())
}

fmt.Print(slc)
}

type st struct{ str string }

func (s *st) String() string { return s.str }

func (s *st) toStringer() fmt.Stringer { return s }


Те из вас кто давно знаком с языком скорее всего подозревают что здесь есть подвох, но вероятно сходу заметить его не смогут. Новички же ждут примерно такой вывод:


[Hello World !]


Пробуем запустить и видим совсем другой ответ: https://go.dev/play/p/5m7YTUeNjfl Возникает вопрос: почему?

В Go внутри цикла, на каждой итерации, испольузется одна и та же переменная. Фактически компилятор переписывает наш код вот в такую конструкцию:


temp := []st{{"Hello"}, {"World"}, {"!"}}

if len(temp) > 0 {
var s st
for i := 0; i < len(temp); i++ {
s = temp[i]
slc = append(slc, s.toStringer())
}
}


Однако это еще не все. Обратите внимание, что toStringer свою структуру получает по указателю. Для нас это происходит неявно, из-за того как работает вызов методов в Go. Дальше он конвертирует себя-по-указателю в тип fmt.Stringer и спокойно возвращает для вставки в слайс.

Проблема тут в том, что мы берем указатель на одну и ту же переменную все три итерации и каждый раз добавляем ее в слайс. И если во втором примере, это еще более менее очевидно, то в первом нужно знать о том, что циклы в Go создают переменную не на итерацию, а на весь цикл сразу. При этом Go тут не одинок - в JS и C# изначально было так же и создатели, впоследствии, решали ровно те же проблемы. В свое время (2012ый год ЕМНИП) Расс Кокс был уверен, что такую ошибку он никогда не допустит. Но сейчас, более десяти лет спустя, он был вынужден признать, что при всем своем опыте и знании Go, он совершил не один десяток подобных ошибок.

Поэтому начиная с версии Go 1.22 переменная цикла будет привязана к конкретной итерации, а не к телу цикла. Это значит, что компилятор будет трансформировать наш код немного иначе:

temp := []st{{"Hello"}, {"World"}, {"!"}}

if len(temp) > 0 {
for i := 0; i < len(temp); i++ {
var v st
v = temp[i]
slc = append(slc, v.toStringer())
}
}


И теперь на каждую итерацию у нас есть своя уникальная переменная. При этом компилятор достаточно умен и для уменьшения потери производительности, будет смотреть в тело цикла. И там где это корректно, будет компилировать код в первый вариант. Первичный анализ показал, что большинство циклов не потеряли в производительности.

Однако это все-же ломающее изменение, поэтому включено оно будет только в проекте где go.mod ставит версию go 1.22.0 и выше и только для самого модуля (модули которые вы импортируете продолжат работать по старому если сами не перейдут на go 1.22).

А попробовать можно уже сейчас:

- На playground через управляющий комментарий (работает только там) либо через Go dev branch. https://go.dev/play/p/QjEKVbvX59-
- Локально, если стоит переменная окружения GOEXPERIMENT=loopvar и у вас Go >= 1.21.0
go.dev Fixing For Loops in Go 1.22 - The Go Programming Language Go 1.21 shipped a preview of a change in Go 1.22 to make for loops less error-prone.
  • ❤ 33
  • 👍 11
  • 🔥 4
More from @go_update
  1. Aug 22, 2026Об изоляции LLM Да, это ещё один пост про работу с Codex/Claude/GLM/Qwen/DeepSeek и прочая…
  2. Aug 11, 2026И вот эти два минуса выглядят нерешенными (на данный момент времени). Можно ли их решить в…
  3. Aug 11, 2026🎂 Вечерний пост о том, что сегодня мне исполнилось 34. Прошёл еще один год, а значит врем…
  4. Jul 13, 2026📝 testing: allow examples with any signature Небольшое «Quality of Life» предложение. Сут…
  5. May 14, 2026📝 net/http/httptest: synctest support Я уже писал про пакет synctest и его возможности. Э…
  6. May 13, 2026Я редко пишу сюда о вещах которые не относятся к Go, но тут у меня появилась хорошая стать…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →