Есть ли foreach в Go? Пошаговый разбор для новичков

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Есть ли foreach в Go? Пошаговый туториал с примерами

Если вы пришли в Go из Python, JavaScript или PHP, то наверняка привыкли к лаконичному foreach для перебора коллекций. И вот вы пишете свой первый цикл на Go и задаётесь вопросом: «А где же мой любимый foreach? Неужели в Go его нет?»

Спойлер: прямого ключевого слова foreach в Go действительно нет. Но есть кое-что гораздо более гибкое и мощное — конструкция for range. В этом туториале мы разберём, как работает итерация в Go, покажем реальные примеры кода и сравним с привычным foreach из других языков.

Почему в Go нет foreach и зачем нужен range?

Go — язык минималистичный. Его создатели намеренно избегали дублирования синтаксических конструкций. Вместо того чтобы добавлять отдельное ключевое слово foreach, они расширили возможности существующего цикла for. Так появился for range — универсальный инструмент для обхода массивов, срезов (slices), карт (maps), строк и даже каналов (channels).

Если вы ищете точный аналог foreach в Go, то вот он — for range.

Синтаксис for range: разбор на пальцах

Базовая структура выглядит так:

for index, value := range collection {
    // тело цикла
}

Где:

  • index — индекс элемента (для массивов и срезов) или ключ (для карт).
  • value — копия значения элемента.
  • collection — ваша коллекция (срез, массив, map, строка или канал).

Важный момент: value — это всегда копия элемента. Если вы измените value внутри цикла, оригинальный элемент не поменяется. Для изменения оригиналов нужно обращаться через индекс.

Пример 1: Обход среза (slice) — аналог foreach в Go

Начнём с самого частого случая — перебор элементов среза:

package main

import "fmt"

func main() {
    fruits := []string{"яблоко", "банан", "вишня", "дыня"}

    fmt.Println("Перебор среза (аналог foreach):")
    for index, fruit := range fruits {
        fmt.Printf("Индекс %d: %s\n", index, fruit)
    }
}

Вывод:

Перебор среза (аналог foreach):
Индекс 0: яблоко
Индекс 1: банан
Индекс 2: вишня
Индекс 3: дыня

Если вам нужен только индекс (например, для изменения элементов), можно опустить значение:

for i := range fruits {
    fruits[i] = "фрукт: " + fruits[i]
}

А если нужны только значения — используйте подчёркивание _:

for _, fruit := range fruits {
    fmt.Println(fruit)
}

Пример 2: Обход map — классический foreach

В Go нет более элегантного способа перебора карт, чем for range:

package main

import "fmt"

func main() {
    ages := map[string]int{
        "Анна": 25,
        "Борис": 30,
        "Виктор": 35,
    }

    fmt.Println("Перебор map (foreach):")
    for name, age := range ages {
        fmt.Printf("%s: %d лет\n", name, age)
    }
}

Порядок обхода map в Go не гарантируется. Если вам нужен стабильный порядок, собирайте ключи в срез и сортируйте его.

Пример 3: Обход строки — посимвольная итерация

В отличие от многих языков, for range в Go итерируется по рунам (unicode-символам), а не по байтам:

package main

import "fmt"

func main() {
    text := "Привет, Go!"

    fmt.Println("Обход строки по символам:")
    for i, r := range text {
        fmt.Printf("Позиция %d: символ '%c' (код %U)\n", i, r, r)
    }
}

Обратите внимание: индекс i указывает на начальный байт руны, а не на порядковый номер символа. Для строк с однобайтовыми символами (ASCII) эти значения совпадают.

Пример 4: Бесконечный цикл и обход канала

for range также работает с каналами. Цикл будет получать значения, пока канал не закроется:

package main

import "fmt"

func main() {
    ch := make(chan string)

    go func() {
        ch <- "сообщение 1"
        ch <- "сообщение 2"
        close(ch)
    }()

    fmt.Println("Чтение из канала:")
    for msg := range ch {
        fmt.Println(msg)
    }
}

Это идеальная замена foreach для асинхронной обработки данных.

Сравнение: foreach в Python vs for range в Go

Давайте посмотрим на параллели, чтобы закрепить понимание:

Задача Python (foreach) Go (for range)
Перебор списка for item in list: for _, item := range slice
Перебор с индексами for i, item in enumerate(list): for i, item := range slice
Перебор словаря for key, val in dict.items(): for key, val := range map
Перебор строки for char in string: for _, r := range string

Как видите, разница минимальна. Go просто использует более явный синтаксис с указанием переменных для индекса и значения.

Частые ошибки новичков при работе с for range

Вот несколько подводных камней, которые стоит знать:

  • Изменение значения не влияет на оригинал. Помните: value — это копия. Для изменения используйте collection[index].
  • Захват переменной цикла в горутинах. Если вы передаёте value в горутину внутри цикла, всегда делайте копию или передавайте как аргумент:
// НЕПРАВИЛЬНО — все горутины увидят последнее значение
for _, v                

Блоги

Рекомендаций по книгам