Ответ
В Go все передается в функцию по значению. Когда вы передаете слайс, копируется его дескриптор (заголовок), а не лежащий в его основе массив данных. Дескриптор содержит указатель на массив, длину (len) и вместимость (cap).
Из-за этой особенности есть несколько способов модификации слайса с видимостью для вызывающей функции.
Способ 1: Вернуть новый слайс (Идиоматичный подход)
Это самый распространенный и безопасный способ. Функция принимает слайс и возвращает его измененную версию. Вызывающий код должен присвоить результат переменной.
// appendToSlice возвращает новый слайс
func appendToSlice(s []int) []int {
// Встроенная функция append может вернуть новый массив, если не хватает capacity
return append(s, 42)
}
func main() {
s := []int{1, 2, 3}
s = appendToSlice(s) // Перезаписываем старый слайс новым
fmt.Println(s) // Вывод: [1 2 3 42]
}
Способ 2: Передать указатель на слайс
Этот способ позволяет изменять сам дескриптор слайса (например, его длину или указатель на массив), что делает изменения видимыми снаружи. Используется реже, в основном в функциях, которым нужно модифицировать несколько слайсов, или для оптимизации производительности в специфичных случаях.
// modifySlice принимает указатель и изменяет слайс по месту
func modifySlice(s *[]int) {
*s = append(*s, 42)
}
func main() {
s := []int{1, 2, 3}
modifySlice(&s) // Передаем адрес переменной слайса
fmt.Println(s) // Вывод: [1 2 3 42]
}
Важный нюанс: изменение элементов существующего слайса
Если вы не меняете длину или вместимость слайса, а только изменяете значения его существующих элементов, то изменения будут видны и без возврата значения или указателя. Это происходит потому, что скопированный дескриптор все еще указывает на тот же самый базовый массив.
func changeFirstElement(s []int) {
if len(s) > 0 {
s[0] = 99
}
}
func main() {
s := []int{1, 2, 3}
changeFirstElement(s)
fmt.Println(s) // Вывод: [99 2 3]
} Ответ 18+ 🔞
А, ну вот, опять про эти ваши слайсы в Go, блядь! Сидишь, думаешь, что всё понял, а потом — хоп! — и нихуя не работает, как ожидал. Давайте разберёмся, как с этим чёртовым дескриптором не облажаться.
Смотри, сука, в чём прикол. Когда ты передаёшь слайс в функцию, копируется не сам массив с данными, а этакая заглушка, дескриптор, блядь. В нём лежит указатель на массив, длина и вместимость. И вот из-за этой хуйни есть три основных подхода, чтобы твои изменения не сгинули в никуда.
Способ 1: Вернуть новый слайс (Нормальный, человеческий способ)
Самый частый и безопасный, ёпта. Функция жрёт слайс, делает с ним что надо и выплёвывает обратно новый. Ты его просто присваиваешь своей старой переменной — и всё, пиздецки просто.
// appendToSlice возвращает новый слайс
func appendToSlice(s []int) []int {
// append может нахуярить новый массив, если места не хватит
return append(s, 42)
}
func main() {
s := []int{1, 2, 3}
s = appendToSlice(s) // Заменяем старый слайс на свежеиспечённый
fmt.Println(s) // Вывод: [1 2 3 42]
}
Способ 2: Тыкать указателем (Для особо хитрых)
Берёшь и суёшь в функцию не слайс, а его адрес, указатель, блядь. Тогда можно менять сам дескриптор — длину, указатель на массив — и это будет видно снаружи. Используется реже, обычно когда надо сразу несколько слайсов перепахать или для микрооптимизаций, которые потом сам же и проклянёшь.
// modifySlice принимает указатель и долбит слайс на месте
func modifySlice(s *[]int) {
*s = append(*s, 42)
}
func main() {
s := []int{1, 2, 3}
modifySlice(&s) // Шлёшь адрес, как письмо счастья
fmt.Println(s) // Вывод: [1 2 3 42]
}
Важный нюанс, блядь: Меняем то, что уже есть
А вот это, сука, ключевой момент! Если ты не лезешь менять длину или вместимость, а просто обновляешь значения в уже существующих ячейках — то всё сработает и так! Потому что скопированный дескриптор всё равно тычет в тот же самый базовый массив, ёпта.
func changeFirstElement(s []int) {
if len(s) > 0 {
s[0] = 99 // Прямо в оригинал, блядь!
}
}
func main() {
s := []int{1, 2, 3}
changeFirstElement(s)
fmt.Println(s) // Вывод: [99 2 3]
}
Вот и вся магия, ебать её в сраку. Главное — понимать, когда ты ковыряешь данные в массиве, а когда пытаешься растянуть или укоротить сам слайс. А то получится как у того Герасима — хотел как лучше, а получилось «Муму» в озеро, пиздец.