Цикл for: все варианты + типичные ошибки
В Go философия простоты доведена до абсолюта - есть только один цикл for. Но не спешите расстраиваться! Он умеет всё, что умеют while, do-while и foreach в других языках.
Классический for
Самый привычный вариант с тремя частями:
for i := 0; i < 10; i++ {
fmt.Println(i)
}
For как while
Можно использовать только условие:
count := 0
for count < 5 {
fmt.Println(count)
count++
}
// Или с более сложным условием
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
line := scanner.Text()
if line == "exit" {
break
}
}
Бесконечный цикл
Просто for без условий:
for {
// Бесконечный цикл
// Выход через break или return
if someCondition {
break
}
}
Range - итерация по коллекциям
Слайсы и массивы
fruits := []string{"яблоко", "банан", "апельсин"}
// Индекс и значение
for i, fruit := range fruits {
fmt.Printf("%d: %s\n", i, fruit)
}
// Только значение (индекс игнорируем через _)
for _, fruit := range fruits {
fmt.Println(fruit)
}
// Только индекс
for i := range fruits {
fmt.Println(i)
}
Строки (по рунам, не байтам!)
word := "Привет"
for i, char := range word {
fmt.Printf("Позиция %d: %c\n", i, char)
}
// Выведет правильно каждую букву, даже кириллицу
Maps
ages := map[string]int{
"Алиса": 30,
"Боб": 25,
}
for name, age := range ages {
fmt.Printf("%s: %d лет\n", name, age)
}
Каналы
ch := make(chan int)
// В другой горутине
go func() {
ch <- 1
ch <- 2
close(ch)
}()
// Читаем из канала до его закрытия
for num := range ch {
fmt.Println(num)
}
Break и Continue
Break - выход из цикла
for i := 0; i < 10; i++ {
if i == 5 {
break // Выходим при i = 5
}
fmt.Println(i)
}
// Выведет: 0 1 2 3 4
Continue - переход к следующей итерации
for i := 0; i < 10; i++ {
if i%2 == 0 {
continue // Пропускаем четные
}
fmt.Println(i)
}
// Выведет: 1 3 5 7 9
Метки (labels)
Для выхода из вложенных циклов:
outer:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == 1 && j == 1 {
break outer // Выход из обоих циклов
}
fmt.Printf("(%d,%d) ", i, j)
}
}
Практические примеры
Поиск в слайсе
func contains(slice []string, target string) bool {
for _, item := range slice {
if item == target {
return true
}
}
return false
}
Фильтрация
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var evens []int
for _, n := range numbers {
if n%2 == 0 {
evens = append(evens, n)
}
}
// evens: [2 4 6 8 10]
Retry с ограничением попыток
maxRetries := 3
for attempt := 1; attempt <= maxRetries; attempt++ {
if err := doSomething(); err == nil {
break // Успех!
}
if attempt == maxRetries {
log.Fatal("Не удалось после", maxRetries, "попыток")
}
time.Sleep(time.Second * time.Duration(attempt))
}
Типичные ошибки
<ComparisonTable data={{ headers: ["Ошибка", "Неправильно", "Правильно"], rows: [ ["Изменение слайса в range", "for i, v := range slice {\n slice[i] = v * 2\n}", "for i := range slice {\n slice[i] *= 2\n}"], ["Захват переменной цикла", "for _, v := range vals {\n go func() { fmt.Println(v) }()\n}", "for _, v := range vals {\n v := v // копия\n go func() { fmt.Println(v) }()\n}"], ["Бесконечный цикл с append", "for i := 0; i < len(s); i++ {\n s = append(s, i)\n}", "length := len(s)\nfor i := 0; i < length; i++ {\n // ...\n}"] ] }} />
Производительность
Pre-allocate слайсы
// Плохо
var result []int
for i := 0; i < 1000; i++ {
result = append(result, i*2)
}
// Хорошо
result := make([]int, 0, 1000)
for i := 0; i < 1000; i++ {
result = append(result, i*2)
}
Избегайте лишних аллокаций
// Плохо - создаёт копию всего слайса
for _, item := range hugeSlice {
process(item)
}
// Хорошо для больших структур - используйте индекс
for i := range hugeSlice {
process(&hugeSlice[i])
}
Полезные паттерны
Sliding window
data := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
windowSize := 3
for i := 0; i <= len(data)-windowSize; i++ {
window := data[i : i+windowSize]
fmt.Println(window)
}
// [1 2 3], [2 3 4], [3 4 5], ...
Обратный цикл
slice := []string{"a", "b", "c", "d"}
// Обратная итерация
for i := len(slice) - 1; i >= 0; i-- {
fmt.Println(slice[i])
}
// d, c, b, a
Итоги
- В Go только один цикл
for, но он универсальный range- мощный инструмент для итерации- Помните про область видимости переменных
- Осторожно с захватом переменных в горутинах
- Pre-allocate слайсы для производительности
В следующем уроке изучим функции - первоклассные объекты в Go!
Типичная ошибка
Писать for i := 0; i < len(s); i++ там, где достаточно range. range чище, безопаснее и читается с первого взгляда.
- Python - Циклы: for, while, range, enumerate, zip - аналогичные паттерны в Python: enumerate вместо индекса, zip для параллельного обхода