Kiedy deklarujesz zmienną, gdzie T
jest jakiś rodzaj:
var name T
Go daje kawałek niezainicjowany „wyzerowany” pamięć.
W przypadku prymitywów oznacza to, że var name int
będzie miało wartość 0, a var name string
będzie "". W C it might be zeroed, or might be something unexpected. Go gwarantuje, że niezainicjowana zmienna jest zerowym odpowiednikiem tego typu.
Wewnętrzne plasterki, mapy i kanały są traktowane jako wskaźniki. Wskaźniki zero mają wartość zerową, co oznacza, że wskazuje zerową pamięć. Bez inicjalizacji możesz napotkać panikę, jeśli spróbujesz na niej działać.
Funkcja make
jest specjalnie zaprojektowana dla plasterka, mapy lub kanału. argumenty aby funkcjonowała są:
make(T type, length int[, capacity int]) // For slices.
make(T[, capacity int]) // For a map.
make(T[, bufferSize int]) // For a channel. How many items can you take without blocking?
plasterki length
to ile przedmiotów zaczyna się. Pojemność to przydzielona pamięć, zanim konieczna będzie zmiana rozmiaru (wewnętrznie, nowy rozmiar * 2, a następnie skopiuj). Aby uzyskać więcej informacji, zobacz Effective Go: Allocation with make.
Struktury: new(T)
jest odpowiednikiem &T{}
, a nie T{}
. *new(T)
jest odpowiednikiem *&T{}
.
Plastry: make([]T,0)
jest odpowiednikiem []T{}
.
Mapy: make(map[T]T)
jest odpowiednikiem map[T]T{}
.
miarę, która jest preferowaną metodą, zadaję sobie następujące pytanie:
Czy znam wartość (y) w tej chwili wewnątrz funkcji?
Jeśli odpowiedź brzmi "tak", to używam jednego z powyższych T{...}
. Jeśli odpowiedź brzmi "nie", to używam make lub new.
Na przykład, chciałbym uniknąć coś takiego:
type Name struct {
FirstName string
LastName string
}
func main() {
name := &Name{FirstName:"John"}
// other code...
name.LastName = "Doe"
}
Zamiast tego chciałbym zrobić coś takiego:
func main() {
name := new(Name)
name.FirstName = "John"
// other code...
name.LastName = "Doe"
}
Dlaczego? Ponieważ, używając new(Name)
, wyraźnie zaznaczam, że I zamierza wypełnić później wartości. Gdybym użył , nie byłoby jasne, że zamierzałem dodać/zmienić wartość później w tej samej funkcji bez czytania reszty kodu.
Wyjątkiem jest struktura, gdy nie chcesz wskaźnika. Będę używał T{}
, ale niczego nie dodam, jeśli chcę dodać/zmienić wartości. Oczywiście działa również *new(T)
, ale to tak, jakby używać *&T{}
. T{}
jest w tym przypadku czystszy, chociaż zwykle używam wskaźników ze strukturami, aby uniknąć kopiowania podczas przekazywania go.
Jeszcze jedna rzecz, o której należy pamiętać: []*struct
jest mniejszy i tańszy w zmianie rozmiaru niż []struct
, zakładając, że struktura jest znacznie większa niż wskaźnik, który zwykle ma 4 - 8 bajtów (8 bajtów na 64-bitowym?).
+1 Nie odpowiada na pytanie, ale na pewno się zgadzam - cieszę się, że tak powiedział Pike. Jest to jedna słabość, którą odkrywam w GoLangu: zbyt wiele sposobów deklarowania i nieczytania korzyści i wad oraz ich stosowności - daje mi czasem poczucie "niezupełnie skończone". – Vector