← Назад к вопросам

Для чего используется слайс?

1.0 Junior🔥 221 комментариев
#Основы Go

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI21 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Для чего используется слайс?

Слайс — один из фундаментальных типов данных в 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 кода.