Struct di Rust
Sebuah struct (struktur) mengelompokkan beberapa nilai yang saling berhubungan ke dalam satu tipe yang punya nama. Kalau tuple adalah bungkusan cepat tanpa nama, struct memberi nama pada setiap field-nya, sehingga kode yang memakainya jauh lebih mudah dibaca.
Mendefinisikan dan membuat struct
Section titled “Mendefinisikan dan membuat struct”struct Person { name: String, age: u32,}
fn main() { let alice = Person { name: String::from("Alice"), age: 30, };
println!("Umur {} {} tahun", alice.name, alice.age);}Output:
Umur Alice 30 tahunBandingkan dengan tuple ("Alice", 30): dengan struct, alice.name dan alice.age sudah menjelaskan dirinya sendiri. Kamu tidak perlu mengingat bahwa field 0 adalah nama dan field 1 adalah umur.
Membuat struct menjadi mutable
Section titled “Membuat struct menjadi mutable”Sama seperti variabel, instance struct secara default bersifat immutable (tidak dapat diubah). Tambahkan mut kalau kamu ingin mengubah sebuah field setelah instance-nya dibuat:
struct Person { name: String, age: u32,}
fn main() { let mut alice = Person { name: String::from("Alice"), age: 30, };
alice.age += 1; println!("Sekarang umur {} {} tahun", alice.name, alice.age);}Menambahkan perilaku dengan method
Section titled “Menambahkan perilaku dengan method”Kamu bisa menempelkan fungsi ke sebuah struct dengan blok impl. Di dalamnya, &self merujuk ke instance tempat method itu dipanggil:
struct Person { name: String, age: u32,}
impl Person { fn greet(&self) { println!("Halo, aku {} dan umurku {} tahun", self.name, self.age); }}
fn main() { let alice = Person { name: String::from("Alice"), age: 30 }; alice.greet();}Output:
Halo, aku Alice dan umurku 30 tahunFungsi bergaya constructor
Section titled “Fungsi bergaya constructor”Pola yang umum adalah membuat fungsi new di dalam impl yang membangun instance untukmu, supaya pemanggilnya tidak perlu menuliskan setiap field satu per satu:
struct Person { name: String, age: u32,}
impl Person { fn new(name: &str, age: u32) -> Person { Person { name: name.to_string(), age } }}
fn main() { let bob = Person::new("Bob", 25); println!("Umur {} {} tahun", bob.name, bob.age);}Perhatikan age yang berdiri sendiri di dalam Person { name: name.to_string(), age }. Kalau sebuah variabel punya nama yang persis sama dengan field-nya, kamu tidak perlu menulis age: age, cukup age saja. Rust menyebut ini field init shorthand (singkatan inisialisasi field).
Mencetak struct dengan derive
Section titled “Mencetak struct dengan derive”Coba jalankan println!("{:?}", alice) pada struct seperti yang ditulis di atas, dan kodenya tidak akan bisa di-compile. Rust tidak tahu cara mencetak tipe buatanmu dalam format debug kecuali kamu memintanya. Tambahkan #[derive(Debug)] di atas struct untuk mendapatkan kemampuan itu secara gratis:
#[derive(Debug)]struct Person { name: String, age: u32,}
fn main() { let alice = Person { name: String::from("Alice"), age: 30 }; println!("{:?}", alice);}Output:
Person { name: "Alice", age: 30 }#[derive(...)] adalah cara Rust membuat fungsionalitas umum untuk sebuah tipe secara otomatis. Kamu akan sering sekali melihat Debug, dan nanti Clone serta yang lainnya, ditambahkan dengan cara ini.
Latihan
Section titled “Latihan”1. Method untuk persegi panjang
Section titled “1. Method untuk persegi panjang”Buat struct Rectangle dengan field width dan height (u32). Beri dua method: area, yang mengembalikan luasnya, dan is_square, yang mengembalikan true kalau kedua sisinya sama panjang. Petunjuk: kedua method hanya membaca field, jadi keduanya menerima &self.
Lihat solusi
struct Rectangle { width: u32, height: u32,}
impl Rectangle { fn area(&self) -> u32 { self.width * self.height }
fn is_square(&self) -> bool { self.width == self.height }}
fn main() { let wide = Rectangle { width: 8, height: 3 }; let square = Rectangle { width: 5, height: 5 };
println!("lebar: luas {}, persegi? {}", wide.area(), wide.is_square()); println!("kotak: luas {}, persegi? {}", square.area(), square.is_square());}Output:
lebar: luas 24, persegi? falsekotak: luas 25, persegi? true2. Rekening bank
Section titled “2. Rekening bank”Buat struct Account dengan owner (String) dan balance dalam satuan sen (u32). Tulis Account::new("Ana") yang memulai saldo dari 0. Lalu setor 50 dan 25 sen dengan mengubah field-nya, dan cetak rekening itu dengan dua cara: dengan kalimat biasa dan dengan {:?}. Petunjuk: variabelnya harus let mut sebelum kamu bisa mengubah account.balance, dan {:?} butuh #[derive(Debug)].
Lihat solusi
#[derive(Debug)]struct Account { owner: String, balance: u32,}
impl Account { fn new(owner: &str) -> Account { Account { owner: owner.to_string(), balance: 0 } }}
fn main() { let mut account = Account::new("Ana"); account.balance += 50; account.balance += 25;
println!("{} punya {} sen", account.owner, account.balance); println!("{:?}", account);}Output:
Ana punya 75 senAccount { owner: "Ana", balance: 75 }new sendiri yang mengisi balance: 0, jadi pemanggilnya tidak mungkin lupa. Itulah salah satu alasan besar kenapa fungsi constructor sangat umum dipakai.
3. Daftar orang
Section titled “3. Daftar orang”Masukkan tiga nilai Person ke dalam sebuah vektor: Ana (34), Ben (16), dan Chen (52). Cetak nama semua orang yang berumur 18 tahun ke atas, lalu cetak siapa yang paling tua. Petunjuk: lakukan perulangan dengan for person in &people, dan simpan orang tertua sejauh ini dengan let mut oldest = &people[0];.
Lihat solusi
struct Person { name: String, age: u32,}
fn main() { let people = vec![ Person { name: String::from("Ana"), age: 34 }, Person { name: String::from("Ben"), age: 16 }, Person { name: String::from("Chen"), age: 52 }, ];
let mut oldest = &people[0]; for person in &people { if person.age >= 18 { println!("{} sudah dewasa", person.name); } if person.age > oldest.age { oldest = person; } }
println!("Paling tua: {} ({})", oldest.name, oldest.age);}Output:
Ana sudah dewasaChen sudah dewasaPaling tua: Chen (52)oldest adalah referensi (&Person) ke dalam vektor, bukan salinan, jadi tidak ada yang di-clone. Setiap kali kita menemukan orang yang lebih tua, kita cukup mengarahkan oldest ke orang itu.
Ringkasan
Section titled “Ringkasan”- Struct mengelompokkan field-field bernama ke dalam satu tipe:
struct Person { name: String, age: u32 }. - Buat instance dengan
Person { name: ..., age: ... }; tandai denganmutkalau nanti ingin mengubah field-nya. - Tambahkan perilaku dengan blok
impl;&selfmerujuk ke instance tempat method dipanggil. - Tambahkan
#[derive(Debug)]supaya struct bisa dicetak dengan{:?}.
Kuis singkat
Section titled “Kuis singkat”Kuis singkat
1. Apa kelebihan utama struct dibandingkan tuple?
2. Apa yang bisa kamu lakukan setelah menambahkan #[derive(Debug)] ke sebuah struct?
3. Pada Person { name: name.to_string(), age }, apa fungsi age yang berdiri sendiri?
Skor: 0 / 3
Bacaan lebih lanjut
Section titled “Bacaan lebih lanjut”- Defining and Instantiating Structs — The Rust Book (bahasa Inggris): juga membahas struct update syntax (
..other), tuple struct, dan unit-like struct. - An Example Program Using Structs — The Rust Book (bahasa Inggris): merombak program persegi panjang dari variabel lepas, ke tuple, lalu ke struct, sehingga kamu bisa melihat kenapa struct membantu.
- Method Syntax — The Rust Book (bahasa Inggris):
&selfvs&mut self, method dengan parameter tambahan, dan associated function sepertinew. - Structures — Rust by Example (bahasa Inggris): contoh yang bisa dijalankan untuk setiap jenis struct, dengan latihan kecil di akhir.
- Derive — Rust by Example (bahasa Inggris): apa saja yang bisa dibuatkan
#[derive(...)]untukmu, sepertiDebug,Clone, danPartialEq.