W Go domyślnie wszystkie argumenty funkcji są przekazywane przez wartość: kopiowana jest wartość zmiennej. Jednak niektóre typy (np. slice'y, mapy, kanały) są „opakowaniami” nad wewnętrznymi strukturami (wskaźnikami). Przekazywanie slice'a przez wartość kopiuje tylko deskriptor slice'a, a nie dane; obie zmienne odnoszą się do tej samej tablicy. W przypadku struktur — kopiowana jest cała struktura.
Jeśli należy uniknąć kopiowania i pracować z oryginalną strukturą, używa się przekazywania przez wskaźnik (*Struct).
type User struct { Name string Age int } func updateUser(u User) { u.Age = 30 // zmieni tylko kopię } func updateUserPtr(u *User) { u.Age = 30 // zmieni oryginał } func main() { u := User{"Ivan", 25} updateUser(u) fmt.Println(u.Age) // 25 updateUserPtr(&u) fmt.Println(u.Age) // 30 }
Czy zmiany w slice'ie przekazanym do funkcji są zawsze widoczne poza funkcją?
Nie!
slice[i] = ...), jest to widoczne na zewnątrz.slice = append(slice, ...)), a wynik nie jest zwracany z funkcji — nowe elementy będą w lokalnej kopii, a ty je stracisz.func addElem(s []int) { s = append(s, 100) } func main() { arr := []int{1,2,3} addElem(arr) fmt.Println(arr) // [1 2 3] — 100 nie zostało dodane }
Historia
W jednym z projektów struktury danych z dużym polem struct (200+ bajtów) były przekazywane przez wartość przez kanały między gorutinami, co powodowało ogromne koszty kopiowania i utraty wydajności. Po przejściu na przekazywanie przez wskaźnik latency zmniejszyło się o rząd wielkości.
Historia
W serwisie logów audytu programista przekazywał mapę (map) między funkcjami bez jawnego klonowania (copy). Zmiany wprowadzone przez jedną funkcję nieoczekiwanie zmieniały dane w innej części programu, co powodowało zamieszanie w logach.
Historia
W funkcji dynamicznego zwiększania slice'a wewnątrz funkcji zapomniano zwrócić nowy slice z powrotem. W rezultacie, zmiany nie były odzwierciedlone w kodzie wywołującym, co doprowadziło do utraty części transakcji. Postanowiono zwracać nowy slice z funkcji.