История вопроса:
Go был спроектирован как язык с явной value semantic: почти всё копируется по значению при передаче, включая структуры (struct), но не указатели и не срезы (slice). Это упростило reasoning и повысило безопасность, но внесло ряд "ловушек".
Проблема:
Часто разработчики ожидают, что при передаче структуры в функцию их изменения видны и "наружу". Но происходит копирование всего содержимого (включая вложенные поля — по значению!). Для срезов и map — другое поведение, где копируется "контейнер", но не "содержимое".
Решение:
Передавайте крупные структуры по указателю, если ожидается изменение. Для slice копируется только дескриптор (length, capacity, pointer), а не содержимое — изменения оригинального среза (через индекс) будут видны внешне. Для struct — копируется всё:
type Point struct { X, Y int } func move(p Point) { p.X = 100 } func movePtr(p *Point) { p.X = 100 } func demo() { pt := Point{10, 10} move(pt) fmt.Println(pt.X) // 10 movePtr(&pt) fmt.Println(pt.X) // 100 }
Ключевые особенности:
Если изменить структуру внутри функции, изменится ли оригинал?
Нет, если структура передана по значению — изменения локальны.
type User struct {Name string} func f(u User) {u.Name = "Ann"}
Если изменить элемент среза внутри функции, изменится ли оригинал?
Да. Срезы — "view" на общий массив. Меняя элемент, меняете и исходные данные.
func f(s []int) {s[0] = 99}
Что будет, если вернуть срез, созданный внутри функции?
Сама "шляпа" среза копируется, но underlying array остаётся доступен. Если не сохранить ссылку снаружи, данные могут быть собраны GC.
В функции обработка структуры User шла по значению — изменения не доходили обратно, баги сложно отлавливать.
Плюсы:
Минусы:
Крупные структуры явно передаются по указателю, а для slices всегда комментируется или проверяется поведение. Нет путаницы, все ожидают value-semantic.
Плюсы:
Минусы: