Встречается на 33% собеседований по Go

Как слайсы работают

Слайсы в Go — это динамические представления массивов, которые позволяют работать с последовательностями данных переменной длины. В отличие от массивов, размер которых фиксирован, слайсы могут изменять свой размер, что делает их более гибкими и удобными для большинства задач. Слайс — это структура, содержащая указатель на первый элемент базового массива, длину (length) и вместимость (capacity). Длина — это количество элементов в слайсе, а вместимость — максимальное количество элементов, которое может вместить слайс без перераспределения памяти.

Как устроен слайс

Слайс состоит из трёх компонентов:

  • Указатель — ссылка на первый элемент слайса в базовом массиве.
  • Длина (length) — количество элементов, доступных в слайсе. Длина не может превышать вместимость.
  • Вместимость (capacity) — общее количество элементов в базовом массиве, начиная с первого элемента слайса. Вместимость показывает, насколько слайс может вырасти без выделения новой памяти.

Эти три значения хранятся в заголовке слайса, который передаётся по значению, но ссылается на общий массив данных.

Создание и инициализация

Слайс можно создать несколькими способами:

  1. Литерал слайса:

    go
    s := []int{1, 2, 3}

    При этом создаётся массив из трёх элементов, и слайс ссылается на него.

  2. Функция make:

    go
    s := make([]int, 5) // длина 5, вместимость 5

    Можно указать длину и вместимость отдельно: make([]int, 3, 5) — длина 3, вместимость 5.

  3. Срез массива:

    go
    arr := [5]int{1, 2, 3, 4, 5}
    s := arr[1:4] // включает элементы arr[1], arr[2], arr[3]

    Срез создаёт слайс, который ссылается на подмножество элементов массива.

Динамическое расширение с помощью append

Основная особенность слайсов — возможность динамически изменять размер с помощью встроенной функции append:

go
s := make([]int, 0, 5)
s = append(s, 1, 2, 3, 4, 5) // добавляет элементы

Если при добавлении элементов вместимость слайса оказывается недостаточной, Go автоматически выделяет новый массив большего размера (обычно удваивая вместимость) и копирует в него все существующие элементы. Это операция может быть затратной, поэтому важно заранее резервировать достаточную вместимость, если ожидается много добавлений.

Работа с памятью и общие данные

Слайсы не хранят данные напрямую — они ссылаются на базовый массив. Это означает, что несколько слайсов могут указывать на одни и те же данные, и изменения через один слайс будут видны через другие. Например:

go
fruits := []string{"apple", "orange", "banana", "grape", "plum"}
fmt.Println(fruits[1:4]) // [orange banana grape]

fruitsSlice := fruits[2:]
fruitsSlice[0] = "strawberry" // изменяет fruits[2]

fmt.Println(fruits) // [apple orange strawberry grape plum]

Это поведение важно учитывать, чтобы избежать неожиданных побочных эффектов. Если нужно создать независимую копию данных, используйте copy или срез с полной копией.

Подводные камни

  • Изменение общих данных: как показано выше, слайсы, ссылающиеся на один массив, влияют друг на друга.
  • Потеря элементов при append: если слайс имеет недостаточную вместимость, append создаёт новый массив, и старые слайсы, ссылающиеся на старый массив, остаются неизменными.
  • Срез с нулевой длиной: s := arr[2:2] создаёт слайс с длиной 0, но вместимостью, равной len(arr)-2. Это может привести к неожиданному поведению при append.

Коротко

  • Слайс — это структура из указателя, длины и вместимости, ссылающаяся на базовый массив.
  • Создаётся через литерал, make или срез массива.
  • append динамически расширяет слайс, при необходимости выделяя новый массив.
  • Несколько слайсов могут разделять один массив, поэтому изменения через один видны другим.
  • Для независимой копии используйте copy или создавайте новый слайс с полным копированием.
как отвечать на вопрос
пример собеседования
фреймворки на собеседовании
типичные вопросы junior
интервью вопросы и ответы