В чем разница слайсов и массивов
В языке 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).
-