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

Можно ли мутировать данные переданного слайса?

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

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

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

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

Мутация данных переданного слайса в Go

Да, мутировать данные переданного слайса можно и нужно, так как слайс в Go является ссылочным типом данных относительно своего внутреннего массива (underlying array). Это один из ключевых аспектов работы со слайсами, который важно понимать во избежание неожиданного поведения программы.

Механизм передачи слайса в функцию

При передаче слайса в функцию или метод передается копия дескриптора слайса (slice header), который содержит:

  • Указатель на массив (pointer to array)
  • Длину (length)
  • Емкость (capacity)
// Структура дескриптора слайса (примерное представление)
type sliceHeader struct {
    ptr      unsafe.Pointer
    len, cap int
}

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

Пример мутации элементов слайса

package main

import "fmt"

func modifySlice(s []int) {
    // Мутация существующих элементов - видна вызывающей стороне
    for i := range s {
        s[i] = s[i] * 2
    }
}

func main() {
    original := []int{1, 2, 3, 4, 5}
    fmt.Println("До модификации:", original) // [1 2 3 4 5]
    
    modifySlice(original)
    fmt.Println("После модификации:", original) // [2 4 6 8 10]
}

Важные нюансы и ограничения

Хотя данные слайса можно мутировать, есть важные особенности:

  1. Изменение длины и емкости не отражается вовне Добавление элементов с помощью append может создать новый массив, если превышена емкость:
func appendToSlice(s []int) {
    // Если capacity недостаточен, создается новый массив
    s = append(s, 100)
    // Эта мутация НЕ будет видна вызывающей стороне,
    // если произошло перевыделение памяти
}

func main() {
    slice := make([]int, 3, 3) // len=3, cap=3
    slice[0], slice[1], slice[2] = 1, 2, 3
    
    appendToSlice(slice)
    fmt.Println(slice) // [1 2 3], а не [1 2 3 100]
}
  1. Переназначение слайса внутри функции Если переназначить слайс внутри функции (не через указатель), внешний слайс не изменится:
func reassignSlice(s []int) {
    // Создается новый дескриптор слайса
    s = []int{10, 20, 30}
}

func main() {
    s := []int{1, 2, 3}
    reassignSlice(s)
    fmt.Println(s) // [1 2 3], а не [10 20 30]
}

Практические рекомендации

  • Для мутации существующих элементов слайса передавайте его напрямую - изменения будут видны вызывающей стороне
  • Для изменения длины (добавления/удаления элементов) возвращайте новый слайс из функции или передавайте слайс по указателю (*[]T)
  • Для гарантированной мутации с возможностью изменения длины используйте указатель на слайс:
func modifySliceWithPointer(s *[]int) {
    *s = append(*s, 100)
    (*s)[0] = 999
}

func main() {
    s := []int{1, 2, 3}
    modifySliceWithPointer(&s)
    fmt.Println(s) // [999 2 3 100]
}
  • Используйте копирование при необходимости избежать побочных эффектов:
func processWithoutSideEffects(s []int) []int {
    // Создаем копию для безопасной работы
    copySlice := make([]int, len(s))
    copy(copySlice, s)
    
    // Работаем с копией
    copySlice[0] = 999
    return copySlice
}

Заключение

Мутация данных переданного слайса возможна и активно используется в Go, но требует понимания внутренней механики. Основное правило: изменения элементов массива видны вызывающей стороне, а изменения дескриптора (длины, емкости) или переназначение слайса - нет, если не используется указатель. Это делает слайсы эффективным инструментом для работы с коллекциями данных без лишнего копирования, но требует внимательности от разработчика.