Definiowanie i tworzenie struktury
Typ struktury to lista nazwanych pól. Deklarujesz go przez type Name struct { ... }, a wartości tworzysz literałem złożonym.
Wynik:
Ana 31
{Name:Ana Email:ana@example.com Age:32 Admin:false}
{Name: Email: Age:0 Admin:false}
main.User{Name:"Bob", Email:"", Age:0, Admin:false}
%+v wypisuje nazwy pól, a %#v składnię Go. To najszybszy sposób na podejrzenie struktury podczas debugowania.
Pola tego samego typu mogą dzielić jedną linię: X, Y float64. Nazwy pól zaczynające się wielką literą są eksportowane (widoczne dla innych pakietów i dla encoding/json); te pisane małą literą nie są.
Formy literałów
User{Name: "Ana", Age: 31} // named fields: order-free, omitted fields are zero
User{"Ana", "a@x.com", 31, false} // positional: every field, in order
&User{Name: "Ana"} // pointer to a new struct
new(User) // pointer to a zeroed struct
Wybieraj nazwane pola. Literały pozycyjne psują się, gdy ktoś doda pole lub zmieni ich kolejność, a go vet ostrzega przed nimi dla typów struktur z innych pakietów (na przykład net/url.Error struct literal uses unkeyed fields). Forma pozycyjna jest w porządku dla malutkich lokalnych typów, takich jak Point{1, 2}.
Brak wartości domyślnych: wartości zerowe i konstruktory
Go nie ma w języku domyślnych wartości pól ani konstruktorów. Każde pole zaczyna od wartości zerowej swojego typu: 0, "", false, nil. Tę lukę wypełniają dwa idiomy.
Spraw, by wartość zerowa była użyteczna. sync.Mutex, bytes.Buffer i strings.Builder działają bez inicjalizacji. Jeśli możesz tak zaprojektować typ, zrób to.
Napisz funkcję NewX, gdy pole potrzebuje niezerowej wartości domyślnej albo trzeba coś zaalokować, na przykład mapę:
NewServer zwraca wskaźnik, bo serwer ma metody, które go modyfikują, i będzie współdzielony. Zwracanie struktury przez wartość też jest częste dla małych niezmiennych typów (time.Date zwraca time.Time).
Wskaźniki do struktur
Przy dostępie do pól Go automatycznie wykonuje dereferencję wskaźników do struktur. Mając p := &User{}, piszesz p.Name, a nie (*p).Name.
Wynik:
{11 12}
{11 12} {0 12}
{11 0}
Struktury są wartościami. Przypisanie i argumenty funkcji je kopiują; wskaźnik pozwala współdzielić jedną. Kopie pole po polu są płytkie: pole typu slice lub mapa w kopii nadal wskazuje na te same dane. Kiedy używać czego, wyjaśnia strona o wskaźnikach.
Struktury anonimowe
Typ struktury można zapisać w miejscu, bez nazwy. Przydaje się to do jednorazowych grupowań, testów sterowanych tabelą i dekodowania JSON, który czytasz tylko raz.
Tabela []struct{ in string; want int } to standardowy kształt testu w Go.
Porównywanie struktur
Struktury można porównywać przez ==, gdy każde pole jest porównywalne. Równość oznacza, że wszystkie pola są równe. Porównywalne struktury mogą też być kluczami mapy.
Dla struktury z polami typu slice lub mapa napisz metodę Equal albo porównuj w testach przez reflect.DeepEqual.
Tagi struktur
Tag to literał stringa po typie pola. Język go ignoruje; biblioteki odczytują go przez refleksję. Najczęściej czyta je encoding/json:
To wypisuje {"id":7,"name":"Mug"}: tag zmienia nazwy pól, omitempty pomija zerowe Price, a nieeksportowane pole jest niewidoczne dla kodera. Tagi stosują konwencję key:"value", a kilka kluczy oddziela się spacjami: `json:"name" db:"user_name"`. Źle sformatowany tag kompiluje się bez problemu i jest po cichu ignorowany, co wyłapuje go vet. Opcje opisuje strona o json.
Puste struktury i kolejność pól
struct{} nie ma pól i zajmuje zero bajtów. Używa się jej jako wartości w zbiorze (map[string]struct{}) i jako sygnału w kanałach (chan struct{}), gdzie liczy się tylko samo zdarzenie.
Kolejność pól wpływa na układ w pamięci: kompilator dopełnia pola do ich wyrównania. Struktura bool, int64, bool zajmuje 24 bajty w systemach 64-bitowych, a int64, bool, bool zajmuje 16. Ma to znaczenie tylko dla typów przechowywanych w dużych ilościach.
Typowe błędy
- Modyfikowanie kopii.
for _, u := range users { u.Age++ }za każdym razem zmienia kopię. Użyj indeksu:users[i].Age++. - Brak inicjalizacji pola mapy. Wartość zerowa struktury zawiera mapę nil; zapis do niej wywołuje panic. Zaalokuj ją w konstruktorze.
- Kopiowanie struktury zawierającej
sync.Mutex. Kopia ma własny stan blokady. Przekazuj takie struktury przez wskaźnik;go vetzgłasza kopię. - Literały pozycyjne dla typów z innych pakietów. Psują się, gdy dojdzie nowe pole.
Najczęściej zadawane pytania
Jak zainicjalizować strukturę w Go?
Literałem złożonym. Zwykle z nazwanymi polami: u := User{Name: "Ana", Age: 31}, a każde pominięte pole dostaje wartość zerową. User{} daje same wartości zerowe, &User{...} daje wskaźnik, a new(User) daje wskaźnik do wyzerowanej struktury.
Czy Go obsługuje wartości domyślne pól struktury?
Nie. Każde pole zaczyna od wartości zerowej swojego typu. Idiomem jest funkcja konstruktora, func NewServer() *Server { return &Server{Port: 8080} }, albo zaprojektowanie typu tak, by jego wartość zerowa od razu była użyteczna, jak w sync.Mutex i bytes.Buffer.
Jak porównać dwie struktury w Go?
Przez ==, jeśli każde pole jest porównywalne. Dwie wartości struktury są równe, gdy wszystkie odpowiadające sobie pola są równe. Struktury z polem typu slice, mapa lub funkcja nie da się porównać przez == (błąd kompilacji); porównaj pola samodzielnie albo w testach użyj reflect.DeepEqual.
Czym są tagi struktur w Go?
To literały stringów po typie pola, na przykład `json:"name,omitempty"`. Kompilator je ignoruje; pakiety takie jak encoding/json, sterowniki baz danych i walidatory odczytują je przez refleksję, aby sterować tym, jak pola są kodowane, nazywane lub sprawdzane.
Jak wypisać strukturę z nazwami pól w Go?
Użyj fmt.Printf("%+v\n", s), co wypisze {Name:Ana Age:31}. %v wypisuje tylko wartości, a %#v wypisuje składnię Go razem z nazwą typu: main.User{Name:"Ana", Age:31}.