Menu

Массивы в Golang: фиксированная длина, копирование и слайсы

Массив в Go имеет фиксированную длину, которая входит в его тип, и копируется при присваивании. Как объявлять, обходить, сравнивать и передавать массивы и почему в коде на Go чаще используют слайсы.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Объявление массива

Тип массива записывается как [N]T: длина, затем тип элементов. Длина фиксируется на этапе компиляции и входит в тип.

Вывод:

[90 0 0 0 75] 5
Tue
[5]int [2 3 5 7 11]
["" "b" "" "d" ""]

Массив всегда инициализирован полностью. Элементы, которые вы не задали, содержат нулевое значение типа элементов: 0, "", false, nil.

Индексация и границы

Индексы идут от 0 до len(a)-1. Константный индекс за границами это ошибка компиляции. Переменный индекс за границами вызывает панику во время выполнения.

var a [3]int
a[5] = 1 // compile error: invalid argument: index 5 out of bounds [0:3]

i := 5
a[i] = 1 // panic: runtime error: index out of range [5] with length 3

Обход

for i, v := range a проходит по элементам по порядку. v это копия элемента, поэтому присваивание v не меняет массив. Пишите через индекс.

У массивов есть ещё одна тонкость: range по значению массива заранее вычисляет его копию. Если тело меняет следующие элементы, v всё равно показывает старые значения. Обход &a или a[:] позволяет избежать копии.

Массивы это значения

Это главное отличие от массивов в C, Java или JavaScript. Присваивание массива копирует все элементы. Передача массива в функцию тоже его копирует.

Вывод:

[1 2 3] [99 2 3]
[1 2 3]
[0 2 3]

Для маленьких массивов копирование дёшево, для больших дорого: передача [1_000_000]int по значению копирует восемь мегабайт. Передавайте указатель (*[N]T) или слайс (a[:]), когда массив большой или функция должна его изменить. Для индексации через указатель на массив явный * не нужен: a[0] работает с *[3]int.

Сравнение массивов и массивы как ключи мапы

Массивы сравнимых элементов поддерживают == и !=. Два массива равны, когда равен каждый элемент. Поэтому они годятся в качестве ключей мапы, а слайсы нет.

Массивы разной длины это разные типы, и сравнить их нельзя вовсе: [3]int{} == [4]int{} не компилируется (mismatched types [3]int and [4]int).

Многомерные массивы

Массив массивов даёт фиксированную сетку. Вся сетка занимает один непрерывный блок памяти.

row[:] превращает строку [3]rune в слайс []rune, который принимает string().

Массивы и слайсы

Массив [N]TСлайс []T
Длинафиксирована, входит в типменяется через append
Присваивание и передачакопирует все элементыкопирует маленький заголовок, элементы общие
Нулевое значениеN нулевых элементовnil, длина 0
==да, поэлементнотолько с nil
Ключ мапыданет
Типичное применениеданные фиксированного размера: хеши, координаты, буферыпочти всё остальное

На практике массивы встречаются в нескольких местах: sha256.Sum256 возвращает [32]byte, таблицы поиска, размер которых никогда не меняется, и базовое хранилище слайса. Срез массива через a[:] или a[1:3] создаёт слайс, который разделяет с массивом память; что значит это разделение, объясняет страница о слайсах.

Преобразование между массивами и слайсами

Преобразование слайса в массив (Go 1.20) или в указатель на массив (Go 1.17) вызывает панику, если слайс короче длины массива: panic: runtime error: cannot convert slice with length 2 to array or pointer to array with length 3.

Частые ошибки

  • Ожидание, что функция изменит массив-аргумент. Она получает копию. Передавайте *[N]T или слайс.
  • Массив там, где нужен слайс. []int{1, 2} это слайс, [2]int{1, 2} это массив. Функция, принимающая []int, не примет [2]int; передайте a[:].
  • Большие массивы в цикле range. for _, v := range bigArray сначала копирует весь массив. Обходите &bigArray или слайс.

Часто задаваемые вопросы

Как объявить массив в Go?

Укажите длину в квадратных скобках перед типом элементов: var a [5]int создаёт пять нулей. Со значениями: a := [3]string{"x", "y", "z"}. Используйте [...], чтобы компилятор посчитал сам: a := [...]int{1, 2, 3} имеет тип [3]int.

Чем массив отличается от слайса в Go?

У массива фиксированная длина, которая входит в его тип ([3]int и [4]int это разные типы), и он копируется при присваивании и передаче. Слайс ([]int) это окно в базовый массив, длина которого может меняться через append, а копирование слайса копирует только окно, но не элементы. В большинстве кода на Go используют слайсы.

Как узнать длину массива в Go?

Встроенной функцией len(a). Для массива это константа времени компиляции, поэтому её можно использовать в константных выражениях. cap(a) возвращает то же число.

Можно ли сравнивать массивы в Go?

Да, через == и !=, если тип элементов сравним. Два массива равны, когда равны все их элементы. Массивы также могут быть ключами мапы. Слайсы через == сравнивать нельзя (кроме как с nil); для них используйте slices.Equal.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ