Struktury map i slice w Go mają ważne cechy kopiowania i semantyki pracy z pamięcią, które często prowadzą do nieoczekiwanego zachowania u niedoświadczonych programistów.
Mimo że Go jest uważany za rygorystyczny język z statycznym typowaniem i brakiem wskaźników domyślnie, map i slice mają wbudowany specjalny model: oba typy to struktury referencyjne. To wprowadza ograniczenia i stwarza wiele niuansów przy kopiowaniu i przekazywaniu tych obiektów.
Kopiowanie map i slice nie prowadzi do głębokiego kopiowania zawartości, a tworzy nowy wskaźnik do tego samego obiektu, co prowadzi do nieoczekiwanych efektów ubocznych przy modyfikacji danych, błędnym zwrocie wartości z funkcji i modyfikacjach. Ponadto, zwrócenie map lub slice jako wynik funkcji może spowodować dodatkowe alokacje lub wycieki pamięci.
b := a[:]). Aby całkowicie skopiować elementy, należy użyć wbudowanej funkcji copy().Przykład poprawnego kopiowania:
// Kopiowanie slice a := []int{1, 2, 3} b := make([]int, len(a)) copy(b, a) // b jest teraz niezależny od a // Kopiowanie map src := map[string]int{"x": 1} dst := make(map[string]int) for k, v := range src { dst[k] = v }
Kluczowe cechy:
slice i map to typy referencyjne, kopiowane przez deskriptor, a nie przez zawartośćCo się stanie, jeśli po prostu przypiszesz jeden map/slice do drugiego, a następnie zmienisz jeden z nich?
Zarówno map, jak i slice będą wskazywać na te same dane w pamięci: zmiana wpłynie na oba obiekty.
Dlaczego podczas zwracania slice lub map z funkcji często mówi się "to jest efektywne pod względem pamięci"?
Ponieważ zwracany jest deskriptor kopii, a nie cała zawartość, dane w stercie żyją dopóki są na nie odwołania.
Czy można za pomocą funkcji copy() wykonać "głębokie" kopiowanie map?
Nie, copy() działa tylko na slice i tablicach, dla map zawsze potrzebna jest pętla.
Programista kopiuje slice lub map przez przypisanie i zmienia kopię dla ochrony przed skutkiem ubocznym:
Zalety:
Wady:
Przed modyfikacją potrzebnych danych używa się copy() dla slice i pętli dla map:
Zalety:
Wady: