В Go по умолчанию все аргументы функций передаются по значению: копируется значение переменной. Но некоторые типы (например, срезы, карты, каналы) являются «обертками» над внутренними структурами (указателями). Передача среза по значению копирует только дескриптор среза, а не данные; обе переменные ссылаются на один и тот же массив. В случае структур — копируется вся структура.
Если требуется избежать копирования и работать с оригинальной структурой, используют передачу по указателю (*Struct).
type User struct { Name string Age int } func updateUser(u User) { u.Age = 30 // изменит только копию } func updateUserPtr(u *User) { u.Age = 30 // изменит оригинал } func main() { u := User{"Ivan", 25} updateUser(u) fmt.Println(u.Age) // 25 updateUserPtr(&u) fmt.Println(u.Age) // 30 }
Являются ли изменения в слайсе, переданном в функцию, всегда видны снаружи функции?
Нет!
slice[i] = ...), оно видно снаружи.slice = append(slice, ...)), а результат не возвращается из функции — новые элементы будут в локальной копии, и вы их потеряете.func addElem(s []int) { s = append(s, 100) } func main() { arr := []int{1,2,3} addElem(arr) fmt.Println(arr) // [1 2 3] — 100 не добавился }
История
В одном из проектов дата структуры с большим полем struct (200+ байт) передавались по значению через каналы между горутинами, что вызывало огромные накладные расходы на копирование и потери производительности. После перехода на передачу по указателю latency уменьшилось на порядок.
История
В сервисе лога аудита разработчик передавал карту (map) между функциями без явного клонирования (copy). Изменения, внесённые одной функцией, неожиданно изменяли данные в другой части программы, вызывая путаницу в логе.
История
В функции динамического увеличения среза внутри функции забыли вернуть новый слайс обратно. В результате, изменения не были отражены в вызывающем коде, что привело к потери части транзакций. Было решено возвращать новый срез из функции.