Mendefinisikan dan membuat struct
Tipe struct adalah daftar field bernama. Kamu mendeklarasikannya dengan type Name struct { ... } dan membuat nilainya dengan composite literal.
Output:
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 mencetak nama field dan %#v mencetak sintaks Go. Keduanya adalah cara tercepat untuk memeriksa isi struct saat debugging.
Field dengan tipe yang sama bisa ditulis dalam satu baris: X, Y float64. Nama field yang diawali huruf besar diekspor (terlihat oleh package lain dan oleh encoding/json); nama yang diawali huruf kecil tidak.
Bentuk literal
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
Pakai nama field. Literal posisional rusak ketika seseorang menambah atau mengubah urutan field, dan go vet memberi peringatan untuk literal posisional pada tipe struct dari package lain (misalnya net/url.Error struct literal uses unkeyed fields). Bentuk posisional boleh dipakai untuk tipe lokal yang sangat kecil seperti Point{1, 2}.
Tidak ada nilai default: zero value dan constructor
Go tidak punya nilai default untuk field dan tidak punya constructor di level bahasa. Setiap field dimulai dari zero value tipenya: 0, "", false, nil. Ada dua kebiasaan untuk mengisi kekosongan itu.
Buat zero value-nya berguna. sync.Mutex, bytes.Buffer, dan strings.Builder semuanya bisa dipakai tanpa inisialisasi. Kalau kamu bisa merancang tipe seperti itu, lakukan.
Tulis fungsi NewX saat sebuah field butuh nilai default yang bukan nol, atau ada sesuatu yang harus dialokasikan, seperti map:
NewServer mengembalikan pointer karena server punya method yang mengubahnya dan akan dipakai bersama. Mengembalikan struct sebagai nilai juga umum untuk tipe kecil yang immutable (time.Date mengembalikan time.Time).
Pointer ke struct
Go otomatis melakukan dereference pada pointer struct saat mengakses field. Dengan p := &User{}, kamu menulis p.Name, bukan (*p).Name.
Output:
{11 12}
{11 12} {0 12}
{11 0}
Struct adalah nilai. Assignment dan argumen fungsi menyalinnya; pointer membaginya. Salinan field demi field bersifat dangkal: field slice atau map di salinan tetap menunjuk ke data yang sama. Lihat pointer untuk tahu kapan memakai yang mana.
Anonymous struct
Tipe struct bisa ditulis langsung tanpa nama. Ini berguna untuk pengelompokan sekali pakai, table-driven test, dan decode JSON yang hanya kamu baca sekali.
Tabel []struct{ in string; want int } adalah bentuk standar sebuah tes di Go.
Membandingkan struct
Struct bisa dibandingkan dengan == jika setiap field-nya bisa dibandingkan. Sama berarti semua field-nya sama. Struct yang bisa dibandingkan juga bisa dipakai sebagai key map.
Untuk struct dengan field slice atau map, tulis method Equal, atau bandingkan di tes dengan reflect.DeepEqual.
Struct tag
Tag adalah string literal setelah tipe sebuah field. Bahasa Go mengabaikannya; library membacanya dengan reflection. Pembaca yang paling umum adalah encoding/json:
Program ini mencetak {"id":7,"name":"Mug"}: tag mengganti nama field, omitempty membuang Price yang bernilai nol, dan field yang tidak diekspor tidak terlihat oleh encoder. Tag mengikuti konvensi key:"value", dengan beberapa key dipisahkan spasi: `json:"name" db:"user_name"`. Tag yang formatnya salah tetap bisa dikompilasi dan diabaikan diam-diam, dan go vet bisa menangkapnya. Halaman JSON membahas opsi-opsinya.
Struct kosong dan urutan field
struct{} tidak punya field dan memakan nol byte. Struct ini dipakai sebagai nilai set (map[string]struct{}) dan sebagai sinyal di channel (chan struct{}), saat yang penting hanya kejadiannya.
Urutan field memengaruhi tata letak memori: compiler menambahkan padding agar setiap field sesuai alignment-nya. Struct berisi bool, int64, bool memakan 24 byte di sistem 64-bit, sedangkan int64, bool, bool memakan 16. Hal ini hanya penting untuk tipe yang disimpan dalam jumlah besar.
Kesalahan umum
- Mengubah salinan.
for _, u := range users { u.Age++ }mengubah salinan setiap kali. Pakai indeksnya:users[i].Age++. - Lupa menginisialisasi field map. Zero value struct berisi map nil; menulis ke map itu akan panic. Alokasikan di constructor.
- Menyalin struct yang berisi
sync.Mutex. Salinannya punya status kunci sendiri. Kirim struct seperti itu lewat pointer;go vetmelaporkan penyalinannya. - Literal posisional untuk tipe dari package lain. Literal itu rusak saat ada field yang ditambahkan.
Pertanyaan yang sering diajukan
Bagaimana cara menginisialisasi struct di Go?
Dengan composite literal. Bentuk yang biasa memakai nama field: u := User{Name: "Ana", Age: 31}, dan field yang tidak kamu tulis mendapat zero value-nya. User{} memberi semua zero value, &User{...} memberi pointer, dan new(User) memberi pointer ke struct yang semua field-nya nol.
Apakah Go mendukung nilai default untuk field struct?
Tidak. Setiap field dimulai dari zero value tipenya. Cara idiomatisnya adalah fungsi constructor, func NewServer() *Server { return &Server{Port: 8080} }, atau merancang tipe sehingga zero value-nya sudah berguna, seperti sync.Mutex dan bytes.Buffer.
Bagaimana cara membandingkan dua struct di Go?
Dengan ==, kalau setiap field-nya bisa dibandingkan. Dua nilai struct sama jika semua field yang bersesuaian sama. Struct yang berisi field slice, map, atau fungsi tidak bisa dibandingkan dengan == (error kompilasi); bandingkan field-nya sendiri, atau pakai reflect.DeepEqual di tes.
Apa itu struct tag di Go?
String literal setelah tipe sebuah field, seperti `json:"name,omitempty"`. Compiler mengabaikannya; package seperti encoding/json, driver database, dan validator membacanya dengan reflection untuk mengatur cara field di-encode, dinamai, atau diperiksa.
Bagaimana cara mencetak struct beserta nama field-nya di Go?
Pakai fmt.Printf("%+v\n", s), yang mencetak {Name:Ana Age:31}. %v hanya mencetak nilainya, dan %#v mencetak sintaks Go termasuk nama tipenya: main.User{Name:"Ana", Age:31}.