← Назад к вопросам

Что можно сделать с nil слайс?

2.0 Middle🔥 72 комментариев
#Основы Go

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Работа с nil-слайсами в Go

nil-слайс в Go — это особое значение, представляющее неинициализированный слайс. В отличие от пустого слайса (make([]int, 0) или []int{}), который инициализирован но не содержит элементов, nil-слайс имеет нулевое значение для указателя на массив, длину 0 и ёмкость 0.

Основные операции с nil-слайсами

1. Проверка на nil

var s []int
if s == nil {
    fmt.Println("Слайс равен nil") // Выполнится
}

s2 := []int{}
if s2 == nil {
    fmt.Println("Не выполнится") // Не выполнится, это пустой слайс
}

2. Чтение и получение длины/ёмкости

var s []int
fmt.Println(len(s)) // 0
fmt.Println(cap(s)) // 0
fmt.Println(s)     // [] (выводится как пустой слайс)

// Доступ по индексу вызовет панику
// s[0] = 1 // panic: runtime error: index out of range

3. Итерация с range Итерация по nil-слайсу безопасна и просто не выполнит тело цикла:

var s []string
for i, v := range s {
    fmt.Println(i, v) // Не выполнится
}

4. Использование в append Одно из наиболее полезных свойств — nil-слайсы можно использовать с функцией append:

var s []int
s = append(s, 1, 2, 3) // Создаётся новый массив
fmt.Println(s)         // [1 2 3]

5. Сравнение с nil

var s1 []int
var s2 []int
fmt.Println(s1 == nil) // true
fmt.Println(s2 == nil) // true
fmt.Println(s1 == s2)  // Ошибка компиляции: слайсы можно сравнивать только с nil

Практические аспекты использования

Преимущества nil-слайсов:

  • Нулевое значение по умолчанию — удобно, когда нужно объявить слайс без немедленной инициализации
  • Экономия памяти — не выделяется память под структуру слайса (хотя это минимальная экономия)
  • Ясность кода — явно показывает, что слайс ещё не инициализирован

Распространённые сценарии использования:

Возврат из функций:

func findEvenNumbers(data []int) []int {
    var result []int // nil-слайс
    for _, v := range data {
        if v%2 == 0 {
            result = append(result, v)
        }
    }
    return result // Возвращает nil если чётных чисел нет
}

// Использование:
evens := findEvenNumbers([]int{1, 3, 5})
if evens == nil {
    fmt.Println("Чётных чисел нет")
}

Ленивая инициализация:

type Processor struct {
    cache []string // nil до первого использования
}

func (p *Processor) Process(items []string) {
    if p.cache == nil {
        p.cache = make([]string, 0, len(items)*2)
    }
    p.cache = append(p.cache, items...)
}

Сравнение с пустым слайсом:

// nil-слайс
var nilSlice []int
// Пустой, но инициализированный слайс
emptySlice := []int{}
emptySlice2 := make([]int, 0)

// JSON-сериализация:
jsonNil, _ := json.Marshal(nilSlice)    // "null"
jsonEmpty, _ := json.Marshal(emptySlice) // "[]"

Важные особенности

Отличие от nil для других типов:

// Для мап:
var m map[string]int
m["key"] = 1 // panic: assignment to entry in nil map

// Для слайсов:
var s []int
s = append(s, 1) // Работает корректно

Преобразование в интерфейс:

var s []int
var i interface{} = s
fmt.Println(i == nil) // false! Интерфейс содержит информацию о типе

var s2 []int
var i2 interface{}
fmt.Println(i2 == nil) // true

Рекомендации по использованию

  1. Возвращайте nil вместо пустого слайса, если это семантически обоснованно (например, "отсутствие результата" vs "пустой результат")
  2. Проверяйте слайсы на nil перед использованием в циклах for с индексацией (но не с range)
  3. Используйте nil-слайсы как нулевое значение для полей структур, которые будут инициализированы позже
  4. Помните о JSON-сериализации — nil-слайс сериализуется как null, а пустой слайс как []

nil-слайс в Go — это не баг, а фича, которая при правильном использовании делает код более выразительным и эффективным. Главное — понимать разницу между "ничего нет" (nil) и "есть пустая коллекция" (empty slice) и использовать соответствующий вариант в зависимости от семантики вашей программы.