Ответ
Ключевая особенность в том, что в Go всегда происходит передача по значению. Однако для ссылочных типов (слайсы, мапы, каналы, а также указатели и функции) этим значением является дескриптор (или заголовок), который указывает на общую базовую структуру данных.
Это создает эффект передачи по ссылке:
-
Общие базовые данные: Копируется только заголовок, а не все данные. Поэтому изменения, сделанные в функции через этот заголовок, отражаются на исходном объекте.
func modifySlice(s []int) { // Эта функция получает копию заголовка слайса, // но он указывает на тот же самый базовый массив. s[0] = 100 } func main() { originalSlice := []int{1, 2, 3} modifySlice(originalSlice) fmt.Println(originalSlice) // Вывод: [100 2 3] }
-
Обязательная инициализация: Нулевое значение для ссылочных типов —
nil
. Попытка записи в неинициализированную мапу вызовет панику.var m map[string]int // m == nil m["key"] = 1 // panic: assignment to entry in nil map // Правильная инициализация m = make(map[string]int) m["key"] = 1 // OK
-
Сравнение только с
nil
: Слайсы, мапы и каналы нельзя сравнивать друг с другом с помощью оператора==
. Их можно сравнивать только сnil
. -
Нюанс с
append
: Если при добавлении элементов в слайс с помощьюappend
его емкости (capacity) не хватает, Go создает новый базовый массив. Все последующие изменения будут применяться к этому новому массиву и не затронут исходный.func addElement(s []int) { // append может создать новый базовый массив s = append(s, 4) s[0] = 999 // Это изменение может не отразиться на оригинале } func main() { slice := make([]int, 3, 3) // len=3, cap=3 slice[0] = 1 addElement(slice) fmt.Println(slice) // Вывод: [1 0 0]. Изменения не видны. }
Поэтому правильной практикой является всегда возвращать слайс из функции, которая его изменяет:
s = addElement(s)
.