Menu

Struct di Golang: Mendefinisikan, Menginisialisasi, Membandingkan, dan Tag

Struct mengelompokkan field bernama menjadi satu tipe. Pelajari cara mendefinisikan dan menginisialisasinya, zero value, pointer ke struct, anonymous struct, perbandingan, struct tag, dan konvensi constructor NewX.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

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 vet melaporkan 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}.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai