Historia pytania:
Go został zaprojektowany jako język z wyraźną semantyką wartości: prawie wszystko jest kopiowane według wartości przy przekazywaniu, w tym struktury (struct), ale nie wskaźniki ani slice'y (slice). Ułatwiło to reasoning i zwiększyło bezpieczeństwo, ale wprowadziło szereg "pułapek".
Problem:
Często programiści oczekują, że zmiany w strukturze przekazywanej do funkcji będą widoczne "na zewnątrz". Ale następuje kopiowanie całej zawartości (w tym pól zagnieżdżonych — według wartości!). Dla slice'ów i map mamy inne zachowanie, gdzie kopiowany jest "kontener", ale nie "zawartość".
Rozwiązanie:
Przekazuj duże struktury przez wskaźnik, jeśli oczekujesz zmiany. Dla slice'ów kopiowany jest tylko deskriptor (length, capacity, pointer), a nie zawartość — zmiany w oryginalnym slice'ie (przez indeks) będą widoczne na zewnątrz. Dla struct — kopiowane jest wszystko:
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 }
Kluczowe cechy:
Czy jeśli zmienisz strukturę wewnątrz funkcji, oryginał się zmieni?
Nie, jeśli struktura została przekazana według wartości — zmiany są lokalne.
type User struct {Name string} func f(u User) {u.Name = "Ann"}
Czy jeśli zmienisz element slice'a wewnątrz funkcji, oryginał się zmieni?
Tak. Slice'y to "widok" na wspólną tablicę. Zmieniając element, zmieniasz również dane źródłowe.
func f(s []int) {s[0] = 99}
Co się stanie, jeśli zwrócisz slice stworzony wewnątrz funkcji?
Sama "główka" slice'a jest kopiowana, ale underlying array pozostaje dostępna. Jeśli nie zachowasz referencji na zewnątrz, dane mogą zostać zebrane przez GC.
W funkcji przetwarzanie struktury User odbyło się według wartości — zmiany nie wracały, błędy trudno wykryć.
Plusy:
Minusy:
Duże struktury są wyraźnie przekazywane przez wskaźnik, a dla slice'ów zawsze komentowane lub sprawdzane jest zachowanie. Nie ma zamieszania, wszyscy oczekują semantyki wartości.
Plusy:
Minusy: