Назад
Golang
Циклы в Go
В Go только один вид цикла - for. Но он закрывает все случаи, которые в других языках решаются через while, do-while, foreach.
Базовый for
Классический трёхкомпонентный цикл, как в C/Java.
for init; condition; post {
// тело
}
Пример:
for i := 0; i < 5; i++ {
fmt.Println(i) // 0 1 2 3 4
}
for как while
Убираем init и post - получаем аналог while.
n := 1
for n < 100 {
n *= 2
}
fmt.Println(n) // 128
Бесконечный цикл
for {
// выполняется вечно
// выход - только через break или return
}
Практический пример - сервер/воркер:
func worker(jobs <-chan int) {
for {
job, ok := <-jobs
if !ok {
return // канал закрыт - выходим
}
process(job)
}
}
for range
Итерация по слайсу. Возвращает index, value.
Срез (slice)
nums := []int{10, 20, 30}
for i, v := range nums {
fmt.Printf("index=%d value=%d\n", i, v)
}
// index=0 value=10
// index=1 value=20
// index=2 value=30
Map
scores := map[string]int{"Alice": 95, "Bob": 87}
for name, score := range scores {
fmt.Printf("%s: %d\n", name, score)
}
⚠️ Порядок итерации по map не гарантирован.
Строка (string)
for i, r := range "hello" {
fmt.Printf("%d: %c\n", i, r)
}
// итерация по рунам (unicode), не байтам
Канал (channel)
ch := make(chan int, 3)
ch <- 1; ch <- 2; ch <- 3
close(ch)
for v := range ch {
fmt.Println(v) // 1 2 3
}
// range по каналу завершится только после close(ch)
Только индекс / только значение
// только индекс
for i := range nums { }
// только значение (индекс игнорируем)
for _, v := range nums { }
Управление циклом
break
Выход из текущего цикла.
for i := 0; i < 10; i++ {
if i == 5 {
break
}
fmt.Println(i) // 0 1 2 3 4
}
continue
Пропуск текущей итерации.
for i := 0; i < 5; i++ {
if i%2 == 0 {
continue
}
fmt.Println(i) // 1 3
}
Labels — выход из вложенных циклов
outer:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if j == 1 {
break outer // выходим из ВНЕШНЕГО цикла
}
fmt.Printf("i=%d j=%d\n", i, j)
}
}
// i=0 j=0
Подводные камни
1. Захват переменной в горутине
// ❌ Неправильно — все горутины напечатают последнее значение i
for i := 0; i < 3; i++ {
go func() {
fmt.Println(i)
}()
}
// ✅ Правильно — передаём i как аргумент
for i := 0; i < 3; i++ {
go func(i int) {
fmt.Println(i)
}(i)
}
Go 1.22+: в новых версиях поведение изменено — переменная цикла создаётся заново на каждой итерации. Но знать про эту проблему всё равно нужно.
2. Изменение слайса во время итерации
// ❌ Не удаляй элементы из слайса в range - работаешь с копией
nums := []int{1, 2, 3}
for _, v := range nums {
nums = append(nums, v*10) // бесконечного цикла не будет, но данные будут неожиданными
}
// ✅ Если нужно собрать новый слайс — используй отдельный
var result []int
for _, v := range nums {
result = append(result, v*10)
}
3. range по map — детерминированность
// Если нужен стабильный порядок — сначала отсортируй ключи
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Println(k, m[k])
}
Давайте подведем итог
- В Go один цикл -
for, но он покрывает все сценарии for range— основной инструмент для слайсов- Следи за захватом переменных в горутинах
- Labels — редкий, но иногда незаменимый инструмент