Ответ
При разработке на Go можно столкнуться с рядом типичных ошибок:
-
Разыменовывание
nil
указателя Это приводит к панике во время выполнения. Классический пример:var p *int fmt.Println(*p) // panic: runtime error: invalid memory address or nil pointer dereference
-
Выход за границы слайса или массива Попытка доступа к элементу по индексу, которого не существует.
arr := []int{1, 2, 3} fmt.Println(arr[3]) // panic: runtime error: index out of range [3] with length 3
-
Гонка данных (Data Race) Возникает, когда две или более горутины одновременно обращаются к одной и той же области памяти, и хотя бы одна из них выполняет запись. Для обнаружения используется флаг
-race
(go run -race main.go
).var counter int go func() { counter++ }() // Запись go func() { counter++ }() // Конкурирующая запись // Для безопасной работы используйте мьютексы (sync.Mutex) или атомарные операции (sync/atomic)
-
Взаимная блокировка (Deadlock) Ситуация, когда горутины бесконечно ожидают друг друга. Например, при записи в небуферизованный канал без читателя или при циклической блокировке мьютексов.
// Пример 1: Блокировка канала ch := make(chan int) ch <- 42 // Deadlock: горутина заблокируется навсегда, если никто не читает из ch // Пример 2: Блокировка мьютексов var mu1, mu2 sync.Mutex go func() { mu1.Lock() mu2.Lock() // ... }() go func() { mu2.Lock() mu1.Lock() // Потенциальный deadlock, если горутины выполнятся одновременно // ... }()
-
Утечки горутин (Goroutine Leaks) Горутина может "зависнуть" в ожидании данных из канала, который никогда не будет закрыт или в который никто не будет писать. Такие горутины продолжают существовать и потреблять память до конца работы программы.
func leak() { ch := make(chan int) go func() { val := <-ch // Эта горутина будет ждать вечно, если в канал ch никто не запишет fmt.Println(val) }() }
-
Неправильная работа с
defer
в циклах Переменные, используемые вdefer
, фиксируются в момент выполненияdefer
, а не в момент его вызова. В циклах это приводит к тому, что все отложенные вызовы будут использовать значение переменной из последней итерации.for i := 0; i < 3; i++ { defer func() { fmt.Println(i) // Выведет "3" три раза }() } // Правильно передавать значение как аргумент: for i := 0; i < 3; i++ { defer func(val int) { fmt.Println(val) // Выведет "2", "1", "0" }(i) }
Важно: Распространенное заблуждение, что
defer
не выполняется при панике. На самом деле,defer
выполняется всегда перед тем, как функция завершит свою работу, в том числе и во время паники. -
Переопределение переменных (Variable Shadowing) Объявление новой переменной с тем же именем во внутреннем блоке, что и во внешнем. Это частый источник багов.
val := 10 if true { val, err := someFunc() // Ошибка: создана новая переменная `val` if err != nil { /* ... */ } // Внешняя переменная `val` осталась без изменений } // Правильно: val, err = someFunc()