Ответ
В Go слайс — это структура-заголовок (slice header), которая передается в функцию по значению.
Этот заголовок содержит три поля:
- Указатель на базовый массив (pointer to underlying array).
- Длина (length) — количество элементов в слайсе.
- Ёмкость (capacity) — количество элементов, которое может поместиться в базовом массиве, начиная с указателя.
Когда вы передаете слайс в функцию, создается копия этого заголовка. Оба заголовка (оригинальный и копия) изначально указывают на один и тот же базовый массив.
Это приводит к двум сценариям:
1. Изменение элементов слайса
Поскольку оба заголовка указывают на один массив, изменение элемента через копию слайса будет видно в оригинале.
func modifyElements(s []int) {
s[0] = 100 // Это изменение отразится на оригинальном слайсе
}
func main() {
originalSlice := []int{1, 2, 3}
modifyElements(originalSlice)
fmt.Println(originalSlice) // Вывод: [100 2 3]
}
2. Изменение самого слайса (например, через append
)
Здесь поведение зависит от ёмкости.
- Если
append
не превышаетcapacity
: Новый элемент добавляется в тот же базовый массив. Длина изменяется только в локальной копии заголовка. Оригинальный слайс не изменится. - Если
append
превышаетcapacity
: Go выделяет новый, больший массив, копирует в него старые элементы и добавляет новый. Локальный заголовок начинает указывать на этот новый массив. Оригинальный слайс остается нетронутым.
func addElement(s []int) {
// Это изменение НЕ отразится на оригинальном слайсе,
// так как s - это копия заголовка.
s = append(s, 4)
fmt.Println("Inside function:", s) // [1 2 3 4]
}
func main() {
originalSlice := []int{1, 2, 3}
addElement(originalSlice)
fmt.Println("Outside function:", originalSlice) // [1 2 3]
}
Рекомендуемые подходы для изменения слайса
Возвращать измененный слайс из функции (самый идиоматичный способ):
func addElementCorrectly(s []int) []int {
return append(s, 4)
}
func main() {
s := []int{1, 2, 3}
s = addElementCorrectly(s) // Переприсваиваем результат
fmt.Println(s) // [1 2 3 4]
}*Передавать указатель на слайс (`[]int`):**
Этот способ менее распространен, но может быть полезен, когда нужно изменить сам заголовок слайса (например, в методах).func addElementWithPointer(s *[]int) {
*s = append(*s, 4)
}
func main() {
s := []int{1, 2, 3}
addElementWithPointer(&s)
fmt.Println(s) // [1 2 3 4]
}