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

Можно ли выполнять append в переданный слайс?

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

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

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

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

Можно ли выполнять append в переданный слайс?

Да, выполнять операцию append в перенеданный слайс в Go можно, но последствия этого действия зависят от текущей ёмкости (capacity) слайса и того, как был передан слайс в функцию. Важно понимать, что слайс — это не массив, а структура данных, состоящая из трёх компонентов: указателя на базовый массив, длины (length) и ёмкости (capacity).

Поведение append в зависимости от ёмкости

При вызове append, Go проверяет, достаточно ли ёмкости базового массива для добавления новых элементов:

  • Если ёмкости хватает, append добавляет элементы в существующий базовый массив, изменяет длину слайса и возвращает обновлённый слайс. Исходный слайс внутри функции отразит эти изменения, так как он ссылается на тот же массив.
  • Если ёмкости не хватает, append создаёт новый базовый массив с увеличенной ёмкостью (обычно по определённому алгоритму роста, например, удвоение для небольших размеров), копирует туда старые элементы, добавляет новые и возвращает слайс, указывающий на этот новый массив. В этом случае исходный слайс вне функции не изменится, если не была выполнена перезапись.

Примеры с кодом

Рассмотрим два случая, которые иллюстрируют ключевые аспекты.

Случай 1: Ёмкости хватает

package main

import "fmt"

func modifySlice(s []int) {
    s = append(s, 4) // Ёмкости хватает, добавляем в тот же массив
    fmt.Println("Внутри функции:", s) 
}

func main() {
    original := make([]int, 3, 10) // Длина 3, ёмкость 10
    original[0], original[1], original[2] = 1, 2, 3
    fmt.Println("До вызова:", original)
    modifySlice(original)
    fmt.Println("После вызова:", original) // Изменения видны, так как массив тот же
}

В этом случае, поскольку ёмкость original равна 10, append добавляет элемент 4 в существующий массив. Длина original остаётся 3 в main, но базовый массив изменился. Если в modifySlice мы изменяем элемент, например, s[0] = 100, то это также отразится в original.

Случай 2: Ёмкости не хватает

package main

import "fmt"

func modifySlice(s []int) {
    s = append(s, 4) // Ёмкости не хватает, создаётся новый массив
    fmt.Println("Внутри функции:", s)
}

func main() {
    original := []int{1, 2, 3} // Длина 3, ёмкость 3
    fmt.Println("До вызова:", original)
    modifySlice(original)
    fmt.Println("После вызова:", original) // Без изменений, так как новый массив
}

Здесь original имеет ёмкость 3, поэтому append создаёт новый массив. Слайс s в функции указывает на новый массив, а original в main остаётся без изменений.

Рекомендации по работе

Чтобы избежать путаницы, при использовании append с переданными слайсами рекомендуется:

  • Всегда использовать возвращаемое значение append, так как оно обновляет слайс (особенно важно при возможном изменении ёмкости).
  • Передавать слайс по указателю, если требуется гарантированно изменить исходный слайс в вызывающем коде (например, func modifySlice(s *[]int)), но это усложняет синтаксис и используется реже.
  • Явно возвращать слайс из функции, например:
func appendToSlice(s []int, values ...int) []int {
    return append(s, values...)
}

Итог

Да, append можно выполнять в переданный слайс, но изменения могут не отразиться на исходном слайсе, если произошло перераспределение памяти из-за недостаточной ёмкости. Понимание внутреннего устройства слайсов и работы append критически важно для написания корректного кода на Go. Всегда обрабатывайте возвращаемое значение append, чтобы предотвратить неожиданные ошибки.