Struct bernama yang kita pelajari di chapter sebelumnya menuntut satu hal dari setiap field: sebuah nama. Untuk data seperti koordinat posisi toko atau komposisi warna merek, nama field seperti first, second, atau red sering tidak menambah informasi — urutan dan tipe sudah bercerita cukup banyak. Menulis Coordinate { first: -6.2, second: 106.8 } lebih bertele-tele daripada manfaatnya. Rust menyediakan jalan tengah bernama tuple struct: tipe yang tetap memiliki nama tipe yang jelas, tetapi field-nya diakses lewat posisi seperti tuple. Pada chapter ini kita akan memakai tuple struct, membongkar isinya lewat pola, dan melihat kekuatan tersembunyinya — kemampuan membedakan dua tipe yang isi bentuknya sama persis.
Kapan Nama Field Menjadi Kebisingan #
Nama field berfungsi sebagai dokumentasi: pembaca kode tahu bahwa price adalah harga dan stock adalah jumlah barang. Kegunaan itu melemah ketika urutan nilai sudah baku dan dikenal semua orang. Titik koordinat selalu dibaca (x, y); warna RGB selalu dibaca (merah, hijau, biru). Pada kasus seperti ini, plain tuple (f64, f64) memang ringkas, tetapi kehilangan sesuatu yang penting: nama tipe. Plain tuple (f64, f64) untuk koordinat dan (f64, f64) untuk hasil pengukuran dianggap tipe yang sama oleh Rust — keduanya bebas tertukar tanpa peringatan.
Tuple struct mengisi celah tersebut. Sintaksnya menggabungkan struct NamaTipe dengan daftar tipe dipisah koma di dalam kurung — hasilnya tipe bernama yang field-nya dinomori mulai dari nol. Dari sisi memori, tuple struct identik dengan struct bernama: nilainya berdampingan di stack mengikuti tipe setiap field.
Mendefinisikan dan Menggunakan Tuple Struct #
Mari kita modelkan dua data toko daring: posisi gudang dan toko dalam koordinat, plus warna identitas merek:
struct Coordinate(f64, f64);
struct RgbColor(u8, u8, u8);
fn main() {
let warehouse = Coordinate(-6.2, 106.8);
let store = Coordinate(-6.9, 107.6);
let dx = warehouse.0 - store.0;
let dy = warehouse.1 - store.1;
let distance = (dx * dx + dy * dy).sqrt();
println!("Jarak gudang ke toko: {:.2} km", distance);
let brand = RgbColor(30, 144, 255);
println!("Warna merek: RGB({}, {}, {})", brand.0, brand.1, brand.2);
}Output yang Dihasilkan:
Jarak gudang ke toko: 1.06 km
Warna merek: RGB(30, 144, 255)Mari kita bedah kode di atas baris per baris:
- Baris 1 (
struct Coordinate(f64, f64);): definisi tuple struct diakhiri titik koma karena tidak memakai blok{ }.Coordinatememiliki dua field tanpa nama — field pertama bertipef64, field kedua jugaf64. - Baris 5-6 (
let warehouse = Coordinate(-6.2, 106.8);): instansiasi tuple struct mirip memanggil fungsi — nilai ditulis dalam kurung sesuai urutan tipe pada definisi.warehousemenyimpan koordinat gudang,storemenyimpan koordinat toko cabang. - Baris 8-9 (
warehouse.0danwarehouse.1): field diakses lewat nomor urut dengan operator titik..0adalah nilai pertama (x),.1nilai kedua (y). Nomor ini bagian dari tipe — menuliswarehouse.2akan langsung ditolak karenaCoordinatehanya punya dua field. - Baris 10 (
let distance = (dx * dx + dy * dy).sqrt();): jarak garis lurus dihitung dengan teorema Pythagoras — akar dari penjumlahan kuadrat selisih koordinat. Method.sqrt()padaf64mengembalikan akar kuadratnya. - Baris 12 (
println!("Jarak gudang ke toko: {:.2} km", distance);): format{:.2}membulatkan tampilan menjadi dua angka desimal, sehingga hasil1.063...tampil sebagai1.06. - Baris 14-15 (
let brand = RgbColor(30, 144, 255);):brandmemakai tuple struct satu baris untuk warna, lalu baris berikutnya membaca ketiga field lewatbrand.0,brand.1, danbrand.2— angka30,144,255dibaca sebagai (merah, hijau, biru) sesuai kesepakatan RGB.
Membongkar Isi dengan Pola #
Mengakses field satu per satu lewat .0, .1, .2 akan melelahkan ketika semua field sekaligus dibutuhkan. Kita bisa membongkar (destructuring) nilai tuple struct ke variabel-variabel terpisah dengan pola let, sama seperti membongkar tuple biasa:
struct RgbColor(u8, u8, u8);
fn main() {
let brand = RgbColor(30, 144, 255);
let RgbColor(red, green, blue) = brand;
println!("R: {}", red);
println!("G: {}", green);
println!("B: {}", blue);
}Output yang Dihasilkan:
R: 30
G: 144
B: 255Penjelasan singkatnya:
- Baris 6 (
let RgbColor(red, green, blue) = brand;): pola di sisi kiri meniru bentuk instansiasi tuple struct. Kompilator mencocokkan pola dengan nilaibrand, lalu mengikat setiap field ke variabel baru —redmenerima30,greenmenerima144,bluemenerima255. - Baris 8-10 (
println!R, G, B): ketiga variabel hasil pembongkaran dipakai seperti variabel biasa. Semua field bertipeu8yangCopy, jadibrandtetap sah dipakai setelah baris ini bila diperlukan.
Nama variabel pada pola bebas kita pilih — di sinilah letak keuntungan kecilnya: meski field struct tidak bernama, hasil pembongkaran tetap bisa mendapat nama yang bermakna di scope baru.
Nama Tipe yang Membedakan Satu Sama Lain #
Kekuatan sebenarnya tuple struct bukan pada ringkasnya sintaks, melainkan pada namanya. Dua tuple struct dengan isi bentuk sama tetap dua tipe yang berbeda — kompilator menolak pertukaran di antara keduanya. Sifat ini sangat berharga untuk satuan pengukuran. Bayangkan program logistik bandara yang mencampur meter dan kaki:
struct Meters(f64);
struct Feet(f64);
fn runway_length(value: Meters) -> f64 {
value.0
}
fn main() {
let wingspan = Feet(32.8);
let length = runway_length(wingspan);
println!("Panjang landasan: {} m", length);
}Pesan error kompilator:
error[E0308]: mismatched types
--> main.rs:11:32
|
11 | let length = runway_length(wingspan);
| ------------- ^^^^^^^^ expected `Meters`, found `Feet`
| |
| arguments to this function are incorrect
|
note: function defined here
--> main.rs:4:4
|
4 | fn runway_length(value: Meters) -> f64 {
| ^^^^^^^^^^^^^ -------------
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0308`.Pesan E0308 menolak Feet masuk ke parameter bertipe Meters meskipun keduanya membungkus f64. Konversi harus dilakukan eksplisit oleh pembuat kode — misalnya menyediakan fungsi feet_to_meters(Feet) -> Meters — sehingga satuan tidak pernah tertukar diam-diam. Plain tuple (f64,) atau f64 telanjang tidak memiliki perlindungan ini: angka 32.8 dapat mengalir ke fungsi mana pun yang menerima f64. Pola pembungkusan satu nilai dengan tuple struct seperti Meters(f64) dikenal luas sebagai newtype pattern; kita akan mengulasnya lebih dalam pada Course Tipe Lanjutan.
Memilih Antara Struct Bernama dan Tuple Struct #
Pedoman praktisnya cukup sederhana. Gunakan struct bernama ketika field lebih dari dua atau makna setiap field tidak terbaca dari urutannya — data pelanggan, item pesanan, dan konfigurasi cocok di sini. Gunakan tuple struct ketika jumlah field sedikit (umumnya satu sampai tiga), urutannya baku atau sudah disepakati, dan nama tipe sudah cukup menjelaskan keseluruhan — koordinat, warna, dan satuan ukur masuk kategori ini. Bila ragu, struct bernama adalah pilihan aman karena namanya menghapus keraguan pembaca kode berikutnya.
Ringkasan #
Tuple struct memberi nama tipe tanpa menuntut nama field: instansiasi memakai kurung bernilai, akses memakai .0, .1, dan seterusnya, serta pembongkaran memakai pola let NamaTipe(a, b) = nilai. Manfaat terbesarnya adalah pembedaan tipe: Meters dan Feet ditolak untuk saling bertukar (E0308) walau sama-sama membungkus f64, sehingga kekeliruan satuan tertangkap saat kompilasi. Ketika data kita hanya berupa penanda tanpa isi sama sekali, bentuk struct yang lebih ringkas lagi menanti di Chapter Unit Struct.