TGViewer
Библиотека Go-разработчика | Golang Библиотека Go-разработчика | Golang @goproglib · 24.1K subscribers
Post #7449 3.93K
🫣 Почему for range иногда работает не так, как ожидаешь

for range выглядит простой конструкцией, но именно с ним связано множество неочевидных ошибок. Разберём три самые распространённые.

1. Переменная цикла содержит копию элемента

При обходе слайса значение элемента записывается в переменную цикла:


users := []User{
{Name: "Alice"},
{Name: "Bob"},
}

for _, user := range users {
user.Name = strings.ToUpper(user.Name)
}


Исходный слайс не изменится. user — это копия структуры, а не сам элемент.

➡️ Если нужно изменить данные, работайте через индекс:


for i := range users {
users[i].Name = strings.ToUpper(users[i].Name)
}


Для слайса указателей поведение будет другим: копируется указатель, а не объект, на который он указывает.

2. Адрес переменной цикла — не адрес элемента

Такой код встречается довольно часто:


var result []*User

for _, user := range users {
result = append(result, &user)
}


Начиная с Go 1.22 при использовании := каждая итерация получает собственную переменную цикла, поэтому указатели больше не будут ссылаться на одну и ту же переменную, как это происходило раньше.

Но &user всё равно указывает на копию элемента, а не на элемент исходного слайса.

➡️ Если нужны адреса элементов, используйте индекс:


for i := range users {
result = append(result, &users[i])
}


Если же переменная объявлена заранее и используется присваивание =, она по-прежнему переиспользуется между итерациями.

3. Изменение коллекции во время обхода

Для слайсов выражение после range вычисляется один раз — до начала цикла. Поэтому количество итераций определяется исходной длиной слайса.


numbers := []int{1, 2, 3}

for _, n := range numbers {
numbers = append(numbers, n)
}


Добавленные элементы в текущий обход автоматически не попадут. Кроме того, append может выделить новый underlying array, поэтому изменять слайс во время обхода стоит с осторожностью.

➡️ С map правила другие:


for key := range cache {
delete(cache, key)
}


Удалять элементы во время обхода разрешено. А вот элементы, добавленные в процессе итерации, могут как попасть в текущий обход, так и быть пропущены — спецификация этого не гарантирует. Порядок обхода map также не определён.

И помните: одновременно читать и изменять map из разных горутин без синхронизации нельзя.


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

📍 Навигация: Вакансии • Задачи • Собесы

🐸 Библиотека Go-разработчика

#GoToProduction
  • 👍 11
  • ❤ 7
  • 🥰 1
  • 😁 1
  • 👾 1
More from @goproglib
  1. Sep 23, 2026💥 TCP/IP: что происходит с данными в сети Когда Go-приложение отправляет данные по сети,…
  2. Sep 22, 2026🔴 Встроенные функции len, make, panic — встроенные функции Go, которые можно использовать…
  3. Sep 21, 2026🤩 Go с нуля: серия базовых шпаргалок Собрали путь от Hello, World! до goroutines, channel…
  4. Sep 20, 2026🤔 Вопрос с собеседования по Go Что выведет программа? ❤️ — [1 2 3 4 5] / [1 2 10] 🔥 — [1…
  5. Sep 19, 2026🛠 Rune — open-source IDE и terminal multiplexer на Go Rune объединяет IDE, терминал и AI-…
  6. Sep 18, 2026🤩 SQLite вместо отдельного брокера в Go Watermill поддерживает SQLite Pub/Sub — сообщения…
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 →