Комментарии (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.