Struct

Mendefinisikan struct dengan field, nilai bawaan, dan sintaks inisialisasi.

Daftar isi

Struct mengelompokkan beberapa nilai yang saling berkaitan ke dalam satu tipe data baru tanpa memerlukan kelas dan tanpa pewarisan. Topik ini membahas deklarasi struct beserta field-nya, pemberian nilai bawaan (default), sintaks inisialisasi .{ }, serta struct yang tidak memiliki field sama sekali. Seluruh konsep dirangkai pada akhir topik dalam sebuah program utuh yang menghitung jarak dua titik koordinat.

Konsep#

Mengelompokkan data ke dalam satu tipe#

Tipe struct adalah tipe data bentukan yang menggabungkan beberapa nilai, disebut field, ke dalam satu unit bernama. Deklarasinya ditulis dengan kata kunci struct, diikuti blok kurung kurawal yang berisi daftar field beserta tipenya. Nama struct dideklarasikan dengan const pada tingkat container, dan setelah itu nama tersebut dipakai sebagai tipe di seluruh berkas.

zig Contoh: deklarasi struct Titik
// tidak-dijalankan
const Titik = struct {
    x: f32,
    y: f32,
};

Pengelompokan semacam ini menggantikan peran kelas pada bahasa berorientasi objek. Zig tidak menyediakan kelas, tidak menyediakan pewarisan, dan tidak menyediakan konstruktor tersembunyi. Hubungan antartipe dibangun dengan komposisi, yaitu menjadikan struct lain sebagai tipe sebuah field. Titik tiga dimensi, misalnya, cukup dideklarasikan sebagai struct baru dengan field tambahan z: f32, tanpa mengubah struct titik dua dimensinya.

Nilai struct juga disalin utuh saat penugasan. Apabila sebuah struct disalin ke variabel lain, kedua nilai itu berdiri sendiri, sehingga perubahan pada salinan tidak pernah berpengaruh pada nilai asalnya. Semantik nilai (value semantics) semacam ini membuat aliran data mudah diikuti karena tidak ada dua nama yang menunjuk nilai yang sama secara diam-diam.

Nilai bawaan pada field#

Setiap field boleh diberi nilai bawaan yang dipakai apabila field tersebut tidak diisi saat inisialisasi. Nilai bawaan ditulis setelah tanda = pada deklarasi field, sehingga satu struct dapat mencampur field yang wajib diisi dan field yang boleh dibiarkan kosong.

zig Contoh: field dengan nilai bawaan
// tidak-dijalankan
const Titik = struct {
    x: f32 = 0,
    y: f32 = 0,
};
// Titik{} menghasilkan titik pada asal koordinat (0, 0)

Dengan kedua nilai bawaan di atas, ekspresi Titik{} menghasilkan titik pada asal koordinat tanpa satu argumen pun. Nilai bawaan juga membuat tipe lebih tahan terhadap perubahan karena menambahkan field baru yang bernilai bawaan tidak merusak inisialisasi yang sudah ada di banyak tempat, dan semua pemanggil lama tetap sah. Nilai bawaan menjadi bagian dari kontrak tipe, sehingga dua struct dengan daftar field yang sama tetapi nilai bawaan yang berbeda tetap merupakan dua tipe yang berbeda. Pada struct Titik, nilai bawaan 0 pada kedua field mencerminkan makna matematisnya, yaitu titik asal.

Sintaks inisialisasi struct#

Inisialisasi struct ditulis dengan tanda titik diikuti kurung kurawal, yaitu .{ }, dengan setiap field diberi nilai melalui nama fieldnya. Nama struct ditulis di depan bentuk tersebut, misalnya Titik{ .x = 3, .y = 4 }. Urutan penulisan field bebas karena setiap nilai terikat pada nama fieldnya, bukan pada posisinya. Literal integer seperti 3 yang diberikan pada field bertipe f32 dikonversi secara otomatis, sehingga .x = 3 dan .x = 3.0 sama-sama sah.

Field yang tidak memiliki nilai bawaan wajib muncul di dalam inisialisasi. Kekurangan satu field saja membuat program gagal dikompilasi dengan pesan error missing struct field lengkap dengan nama field yang tertinggal. Sebaliknya, nama field yang tidak dikenal juga ditolak. Kedua pemeriksaan ini berjalan saat kompilasi, sehingga kesalahan ketik pada nama field tertangkap sebelum program sempat berjalan.

Apabila seluruh field memiliki nilai bawaan, inisialisasi tanpa argumen seperti Titik{} sah digunakan. Bentuk singkat ini lazim untuk struct konfigurasi yang hampir seluruh fieldnya memakai nilai standar.

Struct tanpa field#

Struct boleh dideklarasikan tanpa satu field pun. Deklarasinya hanya berupa kata kunci struct diikuti blok kosong, tipe yang dihasilkan tidak membawa data apa pun, dan ukurannya nol byte. Tipe semacam ini berguna sebagai penanda (marker), misalnya untuk memberi nama pada keadaan kosong atau untuk membedakan dua keadaan pada tipe generik.

Berkas berikut mengukur ukuran struct kosong Penanda dengan fungsi bawaan @sizeOf.

zig penanda.zig
const std = @import("std");

const Penanda = struct {};

pub fn main() void {
    std.debug.print("ukuran Penanda = {d} byte\n", .{@sizeOf(Penanda)});
}
text Keluaran
ukuran Penanda = 0 byte

Keluaran menegaskan bahwa instans Penanda tidak memakan memori sama sekali. Nilai bertipe struct kosong tetap sah untuk dibuat, disimpan, dan diteruskan antar fungsi, hanya saja semua operasi itu tidak menambah satu byte pun pada penggunaan memori. Fungsi yang menerima Penanda tidak menangkap data apa pun, tetapi kehadiran tipe pada parameternya tetap membuat maksud panggilannya eksplisit. Deklarasi seperti ini muncul di banyak kode Zig sebagai tipe penanda yang ringan.

Contoh Lengkap#

Program utuh: jarak dua titik#

Konsep di atas dirangkum dalam program penghitung jarak. Struct Titik menyimpan dua koordinat dengan nilai bawaan nol, fungsi jarak menerima dua Titik sebagai nilai, dan fungsi main menyusun keduanya.

zig jarak-titik.zig
const std = @import("std");

const Titik = struct {
    x: f32 = 0,
    y: f32 = 0,
};

fn jarak(a: Titik, b: Titik) f32 {
    const dx = a.x - b.x;
    const dy = a.y - b.y;
    return @sqrt(dx * dx + dy * dy);
}

pub fn main() void {
    const asal = Titik{};
    const p = Titik{ .x = 3, .y = 4 };

    std.debug.print("asal    = ({d}, {d})\n", .{ asal.x, asal.y });
    std.debug.print("p       = ({d}, {d})\n", .{ p.x, p.y });
    std.debug.print("jarak   = {d}\n", .{jarak(asal, p)});
}
text Keluaran
asal    = (0, 0)
p       = (3, 4)
jarak   = 5

Fungsi jarak menghitung beda koordinat pada masing-masing sumbu, mengkuadratkannya, lalu menarik akar kuadrat dengan @sqrt. Titik asal dibuat tanpa argumen sehingga berada di (0, 0), sedangkan titik p diberi koordinat (3, 4). Jarak keduanya bernilai 5, yaitu pola segitiga 3-4-5 yang mudah diperiksa ulang secara manual. Pemanggilan jarak(asal, p) menerima salinan dari kedua titik, sesuai semantik nilai yang dibahas pada bagian pertama.

Program dijalankan dengan zig run dari direktori tempat berkasnya disimpan.

bash
# jalankan program dari direktori topik
zig run jarak-titik.zig
text Keluaran
asal    = (0, 0)
p       = (3, 4)
jarak   = 5

Keluarannya sama persis karena berkas yang sama dijalankan kembali. Spasi tambahan pada sisi kiri keluaran berasal dari string format di dalam kode dan murni menata perataan kolom.

Kesalahan umum#

Kesalahan pada topik ini umumnya berupa kelalaian kecil yang cepat terdeteksi kompilator. Tiga kasus berikut paling sering muncul saat mulai mendeklarasikan struct.

  1. Field tanpa nilai bawaan tertinggal dari inisialisasi. Kompilator menghentikan program dengan pesan error missing struct field beserta nama fieldnya. Tambahkan field yang tertinggal pada inisialisasi, atau beri nilai bawaan pada deklarasi field tersebut apabila nilai standarnya memang bermakna.
  2. Field diubah melalui pengikatan const. Penugasan seperti c.x = 5 pada struct yang dideklarasikan dengan const ditolak kompilator. Gunakan var untuk struct yang fieldnya akan diubah, dan pertahankan const untuk struct yang tidak akan berubah.
  3. Salinan struct dianggap masih terhubung dengan nilai asalnya. Setelah const b = a, perubahan pada a.x tidak mengubah b.x karena b adalah salinan utuh. Apabila dua nama memang perlu menunjuk nilai yang sama, gunakan pointer secara eksplisit.

Ringkasan#

  • Struct mengelompokkan beberapa field bernama bertipe eksplisit ke dalam satu tipe baru, tanpa kelas dan tanpa pewarisan.
  • Field boleh memiliki nilai bawaan yang dipakai apabila field itu tidak diisi saat inisialisasi.
  • Inisialisasi ditulis dengan .{ } dan diperiksa saat kompilasi, sehingga field yang kurang maupun nama field yang salah tertangkap lebih dulu.
  • Struct tanpa field berukuran nol byte dan berguna sebagai tipe penanda, sedangkan penugasan struct selalu menyalin seluruh nilainya.

Referensi#