Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Для чего используется слайс?
Слайс — один из фундаментальных типов данных в Go. Это динамический массив переменной длины, который в Go используется гораздо чаще чем обычные массивы.
Основные цели использования слайсов
1. Работа с коллекциями переменного размера
Массивы имеют фиксированный размер, слайсы гибкие:
// Массив — размер зафиксирован
var arr [10]int
arr[10] = 20 // PANIC — индекс вне границ
// Слайс — динамический размер
var slice []int
slice = append(slice, 1) // добавляем элемент
slice = append(slice, 2) // добавляем ещё
slice = append(slice, 3) // можно добавлять сколько угодно
fmt.Println(len(slice)) // 3
fmt.Println(cap(slice)) // >= 3
2. Эффективная передача данных между функциями
Слайс передаётся by reference (техничeski it's a header), поэтому не копируется:
// Массив копируется полностью при передаче
func processArray(arr [1000]int) {
// arr — копия, использует много памяти
}
// Слайс передаётся заголовок (3 word'а: ptr, len, cap)
func processSlice(slice []int) {
// эффективнее, нет копирования больших данных
}
arr := [1000]int{} // 8KB памяти
processArray(arr) // копирует 8KB
slice := arr[:] // просто reference
processSlice(slice) // копирует только заголовок (24 byte)
Структура слайса
Внутри слайс состоит из трёх компонентов:
type slice struct {
ptr *T // указатель на первый элемент
len int // текущая длина
cap int // ёмкость буфера
}
Это понимание важно для оптимизации:
// Создание слайса
slice := []int{1, 2, 3}
// ptr указывает на память с [1, 2, 3]
// len = 3
// cap = 3 (или больше)
// После append, если есть место в cap
slice = append(slice, 4)
// len = 4
// cap остаётся прежней (если было место)
// Если append требует больше памяти
slice = append(slice, 5, 6, 7) // требует реаллокации
// cap увеличивается (обычно в 1.5 или 2 раза)
Основные операции со слайсами
Создание слайса:
// Литерал
s1 := []int{1, 2, 3}
// make с длиной
s2 := make([]int, 5) // len=5, cap=5
// make с длиной и ёмкостью
s3 := make([]int, 5, 10) // len=5, cap=10
// make с нулевой длиной
s4 := make([]int, 0, 10) // len=0, cap=10 (для оптимизации)
Slicing (срезы):
arr := []int{10, 20, 30, 40, 50}
s1 := arr[1:3] // [20, 30] — индексы 1 и 2
s2 := arr[:2] // [10, 20] — от начала до индекса 2
s3 := arr[2:] // [30, 40, 50] — от индекса 2 до конца
s4 := arr[:] // весь слайс
// Важно! s1, s2, s3, s4 все указывают на ОДНУ и ту же память
arr[1] = 200
fmt.Println(s1) // [200, 30] — видит изменение
Append:
slice := []int{1, 2}
slice = append(slice, 3) // [1, 2, 3]
slice = append(slice, 4, 5, 6) // [1, 2, 3, 4, 5, 6]
// Добавление другого слайса
more := []int{7, 8}
slice = append(slice, more...) // ... распаковывает слайс
Copy:
src := []int{1, 2, 3, 4, 5}
dst := make([]int, 3)
n := copy(dst, src) // копирует минимум len(dst) или len(src)
fmt.Println(dst) // [1, 2, 3]
fmt.Println(n) // 3 (скопировано элементов)
Реальные примеры использования
1. Обработка данных переменного размера:
func filterNumbers(numbers []int, predicate func(int) bool) []int {
var result []int
for _, num := range numbers {
if predicate(num) {
result = append(result, num)
}
}
return result
}
data := []int{1, 2, 3, 4, 5, 6}
evens := filterNumbers(data, func(n int) bool { return n%2 == 0 })
fmt.Println(evens) // [2, 4, 6]
2. Работа с результатами запросов:
func fetchUsers(ctx context.Context, db *sql.DB) ([]User, error) {
var users []User
rows, err := db.QueryContext(ctx, "SELECT id, name FROM users")
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var u User
rows.Scan(&u.ID, &u.Name)
users = append(users, u)
}
return users, rows.Err()
}
3. Построение комплексных структур:
type Request struct {
Headers []Header // переменное число заголовков
Params []string // переменное число параметров
}
req := &Request{
Headers: []Header{
{Name: "Content-Type", Value: "application/json"},
{Name: "Authorization", Value: "Bearer token"},
},
Params: []string{"search=golang", "limit=10"},
}
Оптимизация с pre-allocation
Если знаешь примерный размер, используй pre-allocation:
// ПЛОХО — много реаллокаций
var slice []int
for i := 0; i < 1000000; i++ {
slice = append(slice, i) // каждый append может требовать реаллокацию
}
// ХОРОШО — одна аллокация
slice := make([]int, 0, 1000000) // cap = 1000000
for i := 0; i < 1000000; i++ {
slice = append(slice, i) // никаких реаллокаций
}
Слайсы и горутины
ВАЖНО! Слайсы небезопасны для конкурентного доступа:
// RACE CONDITION!
data := []int{}
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(v int) {
defer wg.Done()
data = append(data, v) // DANGER: одновременный append может испортить слайс
}(i)
}
wg.Wait()
Решение — используй mutex или channel:
ch := make(chan int, 100)
for i := 0; i < 100; i++ {
go func(v int) {
ch <- v
}(i)
}
var data []int
for i := 0; i < 100; i++ {
data = append(data, <-ch)
}
Когда использовать слайсы
- Коллекции переменного размера
- Передача данных между функциями
- Результаты запросов в БД
- Фильтрация и трансформация данных
- Построение динамических структур
- Работа с varargs функциями
Вывод
Слайсы — это основной инструмент для работы с коллекциями в Go. Они дают гибкость массивов с эффективностью передачи данных. Понимание структуры слайса, операций append и copy, pre-allocation и особенностей concurrency критичны для написания эффективного Go кода.