Нулевые значения (Zero Values)

Нулевые значения переменных и безопасная инициализация в Go

Краткое описание

В этом уроке изучаем концепцию нулевых значений (zero values) в Go: какие значения автоматически получают неинициализированные переменные, почему это безопаснее чем в C/Pascal, и как Go гарантирует отсутствие мусора в памяти. Разбираем дефолтные значения для всех базовых типов данных.

Ключевые концепции

Автоматическая инициализация

В Go каждая переменная автоматически инициализируется нулевым значением её типа, если значение не указано явно:

var age int        // автоматически = 0
var name string    // автоматически = ""
var isActive bool  // автоматически = false

Важно: в памяти не остаётся “мусора” — Go гарантирует безопасную инициализацию.

Отличие от C и Pascal

В C/Pascal:

int x;  // содержит случайное значение из памяти!
printf("%d", x);  // неопределённое поведение

Просто выделяется кусок памяти, который не очищается. Там может быть что угодно.

В Go:

var x int  // гарантированно = 0
fmt.Println(x)  // безопасно, выведет 0

Go всегда инициализирует переменные безопасным значением.

Нулевые значения базовых типов

Целочисленные типы → 0

Все целочисленные типы инициализируются нулём:

var intVar int           // 0
var int8Var int8         // 0
var int16Var int16       // 0
var int32Var int32       // 0
var int64Var int64       // 0
var uintVar uint         // 0
var uint8Var uint8       // 0
var byteVar byte         // 0 (альяс uint8)
var runeVar rune         // 0 (альяс int32)

Вывод:

int: 0
int8: 0
int16: 0
int32: 0
int64: 0
uint: 0
byte: 0
rune: 0

Числа с плавающей точкой → 0.0

var float32Var float32   // 0.0
var float64Var float64   // 0.0

fmt.Printf("float32: %.1f\n", float32Var)  // 0.0
fmt.Printf("float64: %.1f\n", float64Var)  // 0.0

Не NaN или undefined, а именно 0.0.

Комплексные числа → 0+0i

var complex64Var complex64     // (0+0i)
var complex128Var complex128   // (0+0i)

fmt.Printf("complex64: %v\n", complex64Var)    // (0+0i)
fmt.Printf("complex128: %v\n", complex128Var)  // (0+0i)

Действительная часть = 0, мнимая часть = 0.

Логический тип → false

var boolVar bool  // false

fmt.Printf("bool: %t\n", boolVar)  // false

Не null, не nil, а именно false.

Строки → пустая строка “”

var stringVar string  // ""

fmt.Printf("string: '%s'\n", stringVar)      // ''
fmt.Printf("длина: %d\n", len(stringVar))    // 0

Важное отличие: в некоторых языках (JavaScript, Python с None, C# с null) строка может быть null/nil. В Go строка по умолчанию — пустая строка "", а не nil.

Указатели → nil

var pointerVar *int
var pointerToString *string

fmt.Printf("*int: %v\n", pointerVar)               // <nil>
fmt.Printf("nil? %t\n", pointerVar == nil)         // true

fmt.Printf("*string: %v\n", pointerToString)       // <nil>
fmt.Printf("nil? %t\n", pointerToString == nil)    // true

Указатели инициализируются значением nil (не указывают ни на что).

Сложные типы (кратко)

Помимо базовых типов, есть ещё сложные структуры данных, у которых тоже есть нулевые значения:

  • Слайсыnil
  • Мапыnil
  • Каналыnil
  • Функцииnil
  • Интерфейсыnil
  • Структуры → все поля инициализируются своими zero values

О них подробнее будет в отдельных уроках.

Таблица нулевых значений

Тип Zero Value Примечание
int, int8, int16, int32, int64 0 Все целочисленные
uint, uint8, uint16, uint32, uint64 0 Беззнаковые целые
byte 0 Альяс uint8
rune 0 Альяс int32
float32, float64 0.0 Вещественные
complex64, complex128 0+0i Комплексные
bool false Логический
string "" Пустая строка (не nil!)
*T (любой указатель) nil Не указывает ни на что
[]T (слайс) nil Нулевой слайс
map[K]V nil Нулевая мапа
chan T nil Нулевой канал
func nil Нулевая функция
interface{} nil Нулевой интерфейс
struct все поля = zero Рекурсивно

Практические примеры

Полный пример из урока

package main

import "fmt"

func main() {
    fmt.Println("=== Zero Values в Go ===")

    // Целочисленные типы
    var intVar int
    var int8Var int8
    var byteVar byte
    var runeVar rune

    fmt.Println("--- Целочисленные ---")
    fmt.Printf("int: %d\n", intVar)      // 0
    fmt.Printf("int8: %d\n", int8Var)    // 0
    fmt.Printf("byte: %d\n", byteVar)    // 0
    fmt.Printf("rune: %d\n", runeVar)    // 0

    // Вещественные числа
    var float32Var float32
    var float64Var float64

    fmt.Println("\n--- Вещественные ---")
    fmt.Printf("float32: %.1f\n", float32Var)  // 0.0
    fmt.Printf("float64: %.1f\n", float64Var)  // 0.0

    // Комплексные
    var complex64Var complex64
    var complex128Var complex128

    fmt.Println("\n--- Комплексные ---")
    fmt.Printf("complex64: %v\n", complex64Var)     // (0+0i)
    fmt.Printf("complex128: %v\n", complex128Var)   // (0+0i)

    // Логический
    var boolVar bool
    fmt.Println("\n--- Логический ---")
    fmt.Printf("bool: %t\n", boolVar)  // false

    // Строка
    var stringVar string
    fmt.Println("\n--- Строка ---")
    fmt.Printf("string: '%s' (длина: %d)\n", stringVar, len(stringVar))
    // string: '' (длина: 0)

    // Указатели
    var pointerVar *int
    fmt.Println("\n--- Указатели ---")
    fmt.Printf("*int: %v (nil? %t)\n", pointerVar, pointerVar == nil)
    // *int: <nil> (nil? true)
}

Безопасное использование

Можно смело использовать неинициализированные переменные:

var counter int
counter++  // безопасно, counter был 0, стал 1

var message string
message += "Hello"  // безопасно, добавляет к пустой строке

С указателями нужна осторожность:

var ptr *int
// *ptr = 10  // ПАНИКА! nil pointer dereference

// Правильно - сначала инициализировать
var value int = 42
ptr = &value
*ptr = 10  // теперь безопасно

Отличие от других языков

C#

int x;           // 0 (value type)
string s;        // null (reference type)
bool b;          // false

В C# строки — ссылочный тип, поэтому null по умолчанию.

JavaScript

let x;              // undefined
let y = null;       // null (явно)
let z = 0;          // 0 (явно)

JavaScript имеет undefined и null.

Python

x = None           # нет неинициализированных переменных

Python требует явной инициализации.

Go

var x int          // 0 (гарантированно)
var s string       // "" (гарантированно)
var b bool         // false (гарантированно)

Go всегда инициализирует безопасным значением, без null/undefined/None.

Важные моменты

1. Гарантия безопасности Go гарантирует, что переменная всегда имеет определённое значение. Нет случайного мусора из памяти.

2. Нет null для примитивов В Go нет понятия null для базовых типов. Строка всегда существует (может быть пустой), bool всегда true или false.

3. nil только для ссылочных типов nil существует для указателей, слайсов, мап, каналов, функций и интерфейсов — но не для int, string, bool.

4. Проверка на nil для указателей Всегда проверяйте указатели перед разыменованием:

if ptr != nil {
    value := *ptr
}

5. Пустая строка ≠ nil

var s string       // s == ""
s == ""            // true
s == nil           // ОШИБКА компиляции! string не может быть nil

6. Zero value полезен для счётчиков

var count int  // 0 автоматически
for i := 0; i < 10; i++ {
    count++
}

7. Структуры инициализируются рекурсивно

type User struct {
    Name string  // ""
    Age  int     // 0
}

var u User  // Name = "", Age = 0

8. Явная инициализация предпочтительнее Хотя zero values безопасны, в коде лучше явно показывать намерения:

// Менее понятно
var count int

// Более понятно (явное намерение)
var count int = 0

Best Practices

1. Полагайтесь на zero values для простых типов

var counter int     // 0 - подходит для счётчика
var isReady bool    // false - подходит для флага

2. Явно инициализируйте, если zero value не подходит

var retries int = 3        // явно не 0
var status string = "new"  // явно не пустая строка

3. Проверяйте nil для указателей

var ptr *User
if ptr != nil {
    fmt.Println(ptr.Name)
}

4. Используйте композитные литералы для структур

// Вместо
var u User
u.Name = "Иван"
u.Age = 25

// Лучше
u := User{Name: "Иван", Age: 25}

Что запомнить

  • Go автоматически инициализирует все переменные нулевыми значениями
  • Нет “мусора” в памяти (в отличие от C/Pascal)
  • Целые числа: 0
  • Вещественные: 0.0
  • Комплексные: 0+0i
  • Булевы: false
  • Строки: "" (пустая строка, не nil)
  • Указатели: nil
  • Слайсы, мапы, каналы, функции, интерфейсы: nil
  • Структуры: все поля = их zero values
  • nil существует только для ссылочных типов
  • Всегда безопасно использовать неинициализированные переменные (кроме указателей)
  • Проверяйте != nil перед разыменованием указателей
  • Zero values делают код проще и безопаснее
  • Явная инициализация часто улучшает читаемость

Полезные ссылки