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

Как можно работать с переданным слайсом?

2.0 Middle🔥 222 комментариев
#Основы Go

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

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

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

Способы работы с переданным слайсом в Go

В Go слайсы передаются по значению, но это значение содержит указатель на базовый массив, длину и ёмкость. Это создает важные нюансы при работе с переданными слайсами. Рассмотрим ключевые аспекты.

1. Модификация элементов слайса

Поскольку внутри слайса содержится указатель на массив, изменения элементов будут видны вызывающей стороне.

func modifyElements(slice []int) {
    for i := range slice {
        slice[i] *= 2 // Изменения отразятся в оригинальном слайсе
    }
}

func main() {
    nums := []int{1, 2, 3, 4, 5}
    modifyElements(nums)
    fmt.Println(nums) // [2 4 6 8 10]
}

2. Изменение длины и ёмкости (re-slicing)

Операции изменения границ слайса (срезы) работают с тем же базовым массивом:

func resizeSlice(slice []int) []int {
    // Укорачиваем слайс - это влияет только на локальную переменную
    slice = slice[:3]
    
    // Но изменения элементов внутри новой длины сохраняются
    slice[0] = 100
    
    return slice // Возвращаем измененный слайс
}

func main() {
    data := []int{1, 2, 3, 4, 5}
    result := resizeSlice(data)
    fmt.Println(data)   // [100 2 3 4 5] - элемент изменился!
    fmt.Println(result) // [100 2 3]
}

3. Добавление элементов с помощью append

Функция append может работать по-разному в зависимости от ёмкости слайса:

func appendToSlice(slice []int) []int {
    // Если ёмкости достаточно, append модифицирует базовый массив
    // Иначе создается новый массив
    return append(slice, 6, 7, 8)
}

func main() {
    // Вариант 1: Малая ёмкость
    small := make([]int, 2, 3) // Длина 2, ёмкость 3
    small[0], small[1] = 1, 2
    result1 := appendToSlice(small)
    fmt.Printf("small: %v, len: %d, cap: %d\n", small, len(small), cap(small))
    fmt.Printf("result1: %v, len: %d, cap: %d\n", result1, len(result1), cap(result1))
    
    // Вариант 2: Достаточная ёмкость
    large := make([]int, 2, 10) // Длина 2, ёмкость 10
    large[0], large[1] = 1, 2
    result2 := appendToSlice(large)
    fmt.Printf("large: %v\n", large)
    fmt.Printf("result2: %v\n", result2)
}

4. Критические практики работы

Всегда проверяйте, нужен ли возврат значения:

// Плохо: функция вводит в заблуждение
func processSlice(slice []int) {
    slice = append(slice, 10) // Может не изменить оригинал!
}

// Лучше: явный возврат
func processSliceBetter(slice []int) []int {
    return append(slice, 10)
}

// Или: работа только с существующими элементами
func processSliceInPlace(slice []int) {
    for i := range slice {
        slice[i] = process(slice[i])
    }
}

Используйте копирование при необходимости изоляции:

func independentCopy(slice []int) []int {
    // Создаем полностью независимую копию
    newSlice := make([]int, len(slice))
    copy(newSlice, slice)
    
    // Модифицируем копию
    newSlice[0] = 999
    return newSlice // Оригинал не затронут
}

5. Распространенные паттерны

Прием слайса для чтения:

func calculateSum(nums []int) int {
    sum := 0
    for _, v := range nums {
        sum += v
    }
    return sum
}

Модификация "на месте" с возвратом для цепочки вызовов:

func filterEven(numbers []int) []int {
    i := 0
    for _, n := range numbers {
        if n%2 == 0 {
            numbers[i] = n
            i++
        }
    }
    return numbers[:i]
}

Работа со слайсом слайсов:

func transformMatrix(matrix [][]int) {
    for i := range matrix {
        // Каждый слайс-строка передается по тем же правилам
        for j := range matrix[i] {
            matrix[i][j] *= 2
        }
    }
}

Ключевые выводы:

  • Изменения элементов слайса всегда отражаются на оригинале
  • Изменения длины/ёмкости (через append или re-slicing) могут создавать новые массивы
  • Функция append требует возврата результата, так как может вернуть новый слайс
  • Для полной изоляции данных используйте copy() или создание нового слайса
  • Всегда документируйте поведение функции: изменяет ли она слайс, требует ли возврата

Правильное понимание этих механизмов критически важно для написания корректного и эффективного Go-кода, особенно при работе с concurrent-программами, где неожиданное разделение данных может привести к race condition.