Коллекции: массивы, срезы и отображения

Массивы и срезы: создание, индексирование, len, cap, append и range

Содержание курса

Срез []T: литерал, make([]T, len, cap), len, cap и nil-срез

Срез []T — динамическая последовательность. В отличие от массива, у него нет числа в скобках, и его длина не является частью типа: все []int — один и тот же тип, сколько бы элементов в них ни было.

Два способа создать срез — литерал и make.

Литерал:

s := []int{1, 2, 3}
fmt.Println(len(s), cap(s)) // 3 3

Здесь len и cap совпадают, потому что срез создан ровно под три элемента.

make даёт больше контроля:

s := make([]int, 3, 5)
fmt.Println(len(s), cap(s)) // 3 5

Доступны только те индексы, что меньше len: s[0], s[1], s[2] — всё, эти три элементы равны нулю. Попытка обратиться к s[3] или s[4] вызовет панику, потому что они выходят за пределы длины. Правило простое: допустимый диапазон индексов — от 0 до len(s)−1, и никак иначе. append возвращает срез с увеличенной длиной, открывая тем самым новые индексы — но об этом в следующей секции.

Если третий аргумент опустить, cap равен len:

s := make([]int, 4)
fmt.Println(len(s), cap(s)) // 4 4

Инвариант всегда один: len(s) <= cap(s). Это гарантия языка, нарушить её через обычный код невозможно.

Отдельный случай — nil-срез. Это нулевое значение типа []T:

var s []int
fmt.Println(s == nil)       // true
fmt.Println(len(s), cap(s)) // 0 0

nil-срез — не ошибка. Он валиден, его можно передавать в функции и применять к нему append. Просто в нём пока ноль элементов и ноль ёмкости.