логотип PurpleSchool
логотип PurpleSchool

Null, Nil, None, 0 в Go

Автор

Александр Гольцман

В языке программирования Go (или Golang) нет привычного многим разработчикам null, а также не используется None как в Python. Вместо этого существуют свои понятия — nil для указателей, срезов, карт и каналов, а также 0 для числовых типов. В этой статье я расскажу, как Go обрабатывает отсутствие значений, почему здесь нет классического null и что стоит учитывать при работе с nil и нулевыми значениями.

Зачем нужен nil и откуда взялся null

В некоторых языках, например в C, C++ или Java, вы наверняка встречали null — специальное значение, указывающее на отсутствие объекта. В Go же есть своё ключевое слово nil, которое играет роль “пустого” значения для указателей, срезов, карт, каналов и некоторых других типов.

Смотрите, отсутствие классического null для базовых типов (чисел, строк, булевых значений) — это осознанное решение Go. Разработчики Go стремились избежать распространённых ошибок, связанных с null (пустыми ссылками и постоянными проверками на них), упростить код и повысить надёжность.

nil в Go

nil в Go можно встретить у следующих типов:

  • Указатели (например, int)
  • Срезы ([]int)
  • Карты (map[string]int)
  • Каналы (chan int)
  • Функции (если их можно передавать как значения)
  • Интерфейсы (когда у интерфейса нет конкретной реализации)

Когда переменная объявлена, но не инициализирована, она имеет значение nil, если относится к одному из вышеперечисленных типов.

Проверка на nil

Чтобы понять, инициализирована ли переменная, используют простое сравнение:

var ptr *int
if ptr == nil {
    fmt.Println("Указатель не инициализирован")
}

Или для среза:

var nums []int
if nums == nil {
    fmt.Println("Срез пуст")
}

Числовые типы и 0

В Go для числовых типов (int, float64) отсутствует понятие null. Вместо этого они имеют “нулевое” значение по умолчанию — 0 для целых и 0.0 для чисел с плавающей запятой.

Если вам нужно явно указывать, что число “не определено”, можно использовать указатель на число (*int), который может быть nil, или применять другие подходы, например, создавать специальные структуры, указывающие на отсутствие значения.

Строки и пустая строка

Строки в Go не могут быть nil. Если вы объявляете переменную типа string без инициализации, она будет иметь значение "" (пустая строка).

var s string
fmt.Println(s == "") // true

Чтобы указать “отсутствие строки”, используйте указатель на строку (*string), тогда он может быть nil.

Почему нет None или null?

В Python часто используется None, а в JavaScript — null. В Go разработчики пошли другим путём: базовые типы не могут быть nil, а для сложных типов используется nil как признак отсутствия данных. Это снижает риск случайных NullPointerException, типичных для других языков.

Смотрите, это философия Go: лучше явно объявить указатель, если нужно отсутствие объекта, чем иметь “пустое” значение для всего подряд.

Пример использования nil

Покажу на примере:

func main() {
    var nums []int
    if nums == nil {
        fmt.Println("Срез nums не инициализирован")
        nums = []int{1, 2, 3}
    }
    fmt.Println(nums) // [1 2 3]
}

Здесь я проверил, что nums == nil, и инициализировал срез значениями.

Заключение

В Go отсутствуют классические null или None, и это сделано осознанно, чтобы уменьшить вероятность ошибок и упростить код. Вместо них используется nil для сложных типов (указателей, срезов, карт, каналов и интерфейсов) и нулевые значения для базовых типов (числа, строки, булевы переменные).

Давайте подведём итоги:

  • nil — специальное значение для указателей, срезов, карт, каналов и интерфейсов.
  • Числа и строки не могут быть nil, по умолчанию имеют 0 и "".
  • Отсутствие классического null помогает избежать распространённых ошибок, связанных с пустыми ссылками.
  • Для “неопределённых” чисел можно использовать указатель на число (int) или специальные структуры, а не null.

Этот подход делает код Go более надёжным и предсказуемым. Если вам действительно нужна семантика “пустого значения”, используйте указатели или специальные типы.

Стрелочка влевоКонвертация строк в числа в GolangНаименования переменных, функций и структур в GoСтрелочка вправо

Все гайды по Golang

Работа с YAML в GolangПреобразование типов в GolangКонвертация структур в JSON в GolangStrconv в GolangИспользование пакета SQLx для работы с базами данных в GolangРазбираемся с SQL в GolangРазделение строк с помощью функции split в GolangSort в GoПоиск и замена строк в Go - GolangИспользование пакета reflect в GolangРабота с PostgreSQL в GoPointers в GolangПарсинг в GoРабота со списками (list) в GolangПреобразование int в string в GolangРабота с числами с плавающей точкой в GolangРабота с полями в GolangИспользование enum в GolangОбработка JSON в GoЧтение и запись CSV-файлов в GolangРабота с cookie в GolangРегистры в GoКэширование данных в GolangПреобразование byte в string в GolangByte в GoИспользование bufio для работы с потоками данных в GolangДобавление данных и элементов (add) в Go
Логирование в Golang. Zap, Logrus, Loki, GrafanaРабота с Docker-контейнерами в GoИспользование pprof в GolangМеханизмы синхронизации в GolangРабота с пакетом S3 в GolangМониторинг Golang приложений с помощью PrometheusОптимизация проектов на GoПаттерны проектирования в GolangМиграции базы данных в GolangОркестрация контейнеров Go с Kubernetes + DockerGjGo Playground и компилятор GolangИспользование go mod init для создания модулей GolangРабота с переменными окружения (env) в GolangКоманда go build в GolangАвтоматизация Golang проектов — CI/CD с GitLab CI и JenkinsОтладка кода в GolangЧтение и использование конфигурации в приложениях на GolangКомпиляция в GolangКак развернуть Go-приложение на облаке AWSАутентификация в Golang
Сетевые протоколы в GoЗначения в GolangПеременные в GolangДженерик %T и его применение в GolangТипы данных в GolangИспользование tls в GolangИспользование tag в структурах GolangSwitch в GoСтроки в GolangРабота с потоками (stream) в GolangSelect в GoРуны в GoРабота с пакетом params в GolangКонвертация строк в числа в GolangNull, Nil, None, 0 в GoНаименования переменных, функций и структур в GoInt в GolangУстановка GolangЧтение и установка HTTP заголовков в GolangMethods в GolangGoLand — IDE для разработки на Golang от JetBrainsFloat в GolangОбработка «not found» в GolangФлаги командной строки в Go (Golang)Запуск внешних команд в GolangОбработка ошибок в GoИспользование defer в GolangЗначения default в GolangГенерация кода в GoФорматирование кода в GolangЧистая архитектура в GolangКаналы (channels) в GolangПолучение body из HTTP запроса в Golang
Открыть базу знаний