Typed nil
Ночью упавший S3-клиент прошёл все проверки и уронил обработчик загрузки. Утром команда разбирала инцидент и нашла причину: пустой клиент влез в интерфейс, а тот считал его настоящим.
интерфейс, внутри которого лежит nil-указатель конкретного типа, не равен nil — и проверка if err != nil на нём срабатывает на пустом месте.
Nil-указатель в интерфейсе
var client *S3Client
var st Storage = client // st не nil!
fmt.Println(st == nil) // false
Интерфейс — пара «тип + значение» из прошлого урока (в памяти — два слова). st хранит: тип *S3Client и данные nil. Сам интерфейс при этом не nil: он знает, какого типа значение внутри, просто значение — пустое. Именно так упавший клиент хранилища прошёл все проверки в проде — а потом уронил обработчик, когда тот обратился к пустым данным.
Классическая ловушка с ошибками
error — это интерфейс с одним методом Error string (полный разговор — в). Чтобы ловушка сработала, нужен тип, который его реализует:
// отдельный тип ошибки, а не запись о файле как ошибка
type fileErr struct{}
func (*fileErr) Error() string { return "файл не найден" }
func findFile(name string) error {
var err *fileErr
if something {
err = &fileErr{}
}
return err // вернули типизированный nil как ошибку
}
err := findFile("report.pdf")
if err != nil {
// сработает, хотя реальной ошибки нет!
}
return err упаковывает (*fileErr)(nil) в error: тип есть, данные пустые. Наверху err != nil — истина, и вызывающий обрабатывает ошибку, которой не было. Это одна из самых известных ловушек Go.
Как лечить
Лекарство — возвращать голый nil, а не типизированный:
func findFile(name string) error {
var err *fileErr
if something {
err = &fileErr{}
}
if err == nil {
return nil // голый nil — интерфейс станет по-настоящему nil
}
return err
}
Правило простое: функция, возвращающая интерфейс, должна возвращать nil как голый nil, а не nil-указатель конкретного типа. Если ты видишь if err != nil, срабатывающее «на пустом месте» — ищи функцию, которая вернула типизированный nil.
Вы узнаете:
- почему
fmt.Printlnна typed nil печатает<nil> - почему компилятор не защищает от typed nil
Интерфейс с nil-указателем внутри ≠ nil: тип заполнен, данные пустые. Лечится возвратом голого nil.
10 000 ₽ · 60 минут · онлайн