В языке Go массивы и слайсы представляют собой структуры данных, предназначенные для хранения последовательностей элементов одного типа. Однако, между ними существует принципиальное различие в размере, передаче по значению, гибкости, внутреннем устройстве и практическом применении.
1. Определение и синтаксис
Массив (Array)
Массив — это фиксированная последовательность элементов одного типа, определяемая длиной на момент объявления.
var arr [3]int = [3]int{1, 2, 3}
Или сокращённо:
arr := [3]int{1, 2, 3}
Длина массива — часть его типа. Массивы [3]int и [4]int — разные типы.
Слайс (Slice)
Слайс — это гибкая, динамическая обёртка над массивом. Он не хранит данные сам по себе, а ссылается на массив.
slice := []int{1, 2, 3}
Или из массива:
arr := [5]int{10, 20, 30, 40, 50}
slice := arr[1:4] // элементы 20, 30, 40
2. Фиксированность длины
-
Массив имеет жёстко заданную длину.
-
Изменить размер нельзя.
-
Размер входит в тип.
-
-
Слайс может изменять длину (через append) в пределах вместимости (capacity).
arr := [3]int{1, 2, 3}
// arr = append(arr, 4) // ошибка
slice := []int{1, 2, 3}
slice = append(slice, 4) // OK: [1 2 3 4]
3. Передача в функции (по значению / ссылке)
Массив — по значению
При передаче массива в функцию — создаётся копия:
func modifyArray(a [3]int) {
a[0] = 100
}
arr := [3]int{1, 2, 3}
modifyArray(arr)
fmt.Println(arr) // [1 2 3]
Массив не изменится.
Слайс — по значению, но содержит ссылку на массив
При передаче слайса копируется только структура: указатель, длина, вместимость. Данные — общие.
func modifySlice(s []int) {
s[0] = 100
}
slice := []int{1, 2, 3}
modifySlice(slice)
fmt.Println(slice) // [100 2 3]
Слайс указывает на общий массив — изменения видны.
4. Тип данных
-
[3]int — это конкретный тип массива из 3 int.
-
[]int — это тип слайса, без фиксированной длины.
func takesArray(a [3]int) {}
func takesSlice(s []int) {}
takesArray([3]int{1, 2, 3}) // OK
// takesArray([]int{1, 2, 3}) // ошибка
takesSlice([]int{1, 2, 3}) // OK
5. Внутреннее устройство
Массив:
-
Представляет собой непрерывный блок памяти фиксированной длины.
-
Сам содержит все элементы.
Слайс:
Слайс — это структура из 3 полей:
type slice struct {
data \*T // указатель на первый элемент массива
len int // длина (доступное количество элементов)
cap int // вместимость (размер массива)
}
6. Инициализация
Массив:
arr := [5]int{} // все значения 0
Можно указать значения не по порядку:
arr := [5]int{2: 10, 4: 20} // [0 0 10 0 20]
Слайс:
slice := make([]int, 5) // длина 5, заполнено нулями
slice2 := make([]int, 2, 5) // длина 2, capacity 5
7. Операции и функции
len() и cap()
arr := [4]int{1, 2, 3, 4}
fmt.Println(len(arr)) // 4
slice := arr[1:3]
fmt.Println(len(slice)) // 2
fmt.Println(cap(slice)) // 3 — от среза до конца массива
append() — работает только со слайсами
s := []int{1, 2}
s = append(s, 3, 4)
При превышении cap, создаётся новый массив.
8. Изменение данных
Слайс может изменить элементы массива:
arr := [4]int{1, 2, 3, 4}
s := arr[1:3] // [2, 3]
s[0] = 200
fmt.Println(arr) // [1 200 3 4]
Слайс не копирует элементы — это представление массива.
9. Массивы в структурах и интерфейсах
-
Массивы в Go не так часто используются напрямую из-за своей негибкости.
-
Их можно использовать как поля структур, например, [16]byte в net.IP.
type UUID [16]byte
- Слайсы же чаще применяются при работе с данными переменной длины — строки, буферы, коллекции и т.д.
10. Сравнение массивов и слайсов
| Характеристика | Массив [N]T | Слайс []T |
|---|---|---|
| Длина | Фиксированная | Изменяемая (через append) |
| --- | --- | --- |
| Типизация | Часть типа | Тип не включает длину |
| --- | --- | --- |
| Передача в функцию | Полная копия | Копия структуры, но общие данные |
| --- | --- | --- |
| Возможность расширения | Нет | Да |
| --- | --- | --- |
| Использование памяти | Хранит данные напрямую | Ссылка на массив |
| --- | --- | --- |
| Удобство | Менее гибкий | Гибкий и универсальный |
| --- | --- | --- |
| Совместимость | Только точное совпадение | Совместим с любым слайсом того же типа |
| --- | --- | --- |
| Функция append | Неприменим | Применим |
| --- | --- | --- |
| Методы len, cap | Да | Да |
| --- | --- | --- |
11. Преобразование между массивами и слайсами
Из массива в слайс:
arr := [5]int{1, 2, 3, 4, 5}
s := arr[1:4] // [2 3 4]
Из слайса в массив:
s := []int{1, 2, 3}
var a [3]int
copy(a[:], s) // копирует первые 3 элемента
Нельзя напрямую присвоить слайс массиву, потому что у них разные типы.
12. Сравнение значений
Массивы можно сравнивать напрямую (если элементы сравнимы):
a1 := [3]int{1, 2, 3}
a2 := [3]int{1, 2, 3}
fmt.Println(a1 == a2) // true
Слайсы нельзя сравнивать напрямую (кроме nil):
s1 := []int{1, 2, 3}
s2 := []int{1, 2, 3}
// fmt.Println(s1 == s2) // ошибка компиляции
Для сравнения слайсов нужно использовать reflect.DeepEqual или ручную проверку.
13. Срезы слайсов
Можно делать срезы от слайсов:
s := []int{1, 2, 3, 4, 5}
sub := s[1:4] // [2, 3, 4]
Это создаёт новый слайс, указывающий на ту же память.
14. Nil-слайс и пустой слайс
var s1 []int // nil-слайс
s2 := []int{} // пустой слайс
fmt.Println(s1 == nil) // true
fmt.Println(s2 == nil) // false
Оба считаются пустыми (len == 0), но ведут себя немного по-разному в JSON, API и т.д.
15. Память и управление capacity
При добавлении элементов через append, если вместимость недостаточна — создаётся новый массив с увеличенным cap. Стратегия роста — обычно удвоение.
s := []int{1, 2, 3}
fmt.Println(cap(s)) // 3
s = append(s, 4) // создаётся новый массив
fmt.Println(cap(s)) // обычно 6
Если важно управлять этим вручную — используйте make([]T, len, cap).
16. Применение на практике
-
Массивы — редко используются явно. Применяются в системных библиотеках, криптографии, IP/UUID.
-
Слайсы — основной способ хранения коллекций в Go:
-
динамические массивы,
-
очереди/стеки,
-
буферы,
-
JSON-структуры,
-
строки по символам (через []rune или []byte).
-