Enum di Rust
Sebuah enum (singkatan dari enumeration, atau enumerasi) mendefinisikan tipe yang nilainya bisa berupa salah satu dari sekumpulan pilihan yang tetap. Kalau struct dipakai untuk “benda ini punya semua field ini”, enum dipakai untuk “benda ini pasti salah satu dari pilihan-pilihan ini”.
Mendefinisikan dan memakai enum
Section titled “Mendefinisikan dan memakai enum”enum Direction { North, South, East, West,}
fn main() { let heading = Direction::North;
match heading { Direction::North => println!("Menuju utara"), Direction::South => println!("Menuju selatan"), Direction::East => println!("Menuju timur"), Direction::West => println!("Menuju barat"), }}Output:
Menuju utaraPerhatikan bahwa match di sini tidak butuh cabang _ untuk menangkap sisanya. Karena setiap kemungkinan Direction sudah ditulis, Rust sudah tahu bahwa match-nya lengkap.
Enum bisa menyimpan data
Section titled “Enum bisa menyimpan data”Di sinilah enum di Rust melangkah lebih jauh daripada enum biasa di bahasa lain: setiap varian bisa membawa datanya sendiri:
enum Shape { Circle(f64), // jari-jari Rectangle(f64, f64), // lebar, tinggi}
fn area(shape: &Shape) -> f64 { match shape { Shape::Circle(radius) => 3.14159 * radius * radius, Shape::Rectangle(width, height) => width * height, }}
fn main() { let circle = Shape::Circle(3.0); let rectangle = Shape::Rectangle(4.0, 5.0);
println!("{:.2}", area(&circle)); println!("{:.2}", area(&rectangle));}Output:
28.2720.00Shape::Circle membawa satu f64 (jari-jarinya); Shape::Rectangle membawa dua. match langsung mengeluarkan data itu untukmu di setiap cabangnya, tanpa perlu langkah terpisah untuk “membukanya”.
Enum juga bisa punya method
Section titled “Enum juga bisa punya method”Sama seperti struct, enum bisa punya blok impl berisi method. Di dalam method, biasanya kamu memakai match self untuk menentukan apa yang dilakukan untuk setiap varian:
#[derive(Debug)]enum Light { Red, Yellow, Green,}
impl Light { fn seconds(&self) -> u32 { match self { Light::Red => 30, Light::Yellow => 3, Light::Green => 25, } }
fn next(&self) -> Light { match self { Light::Red => Light::Green, Light::Green => Light::Yellow, Light::Yellow => Light::Red, } }}
fn main() { let mut light = Light::Red;
for _ in 0..4 { println!("{:?} selama {} detik", light, light.seconds()); light = light.next(); }}Output:
Red selama 30 detikGreen selama 25 detikYellow selama 3 detikRed selama 30 detiknext mengembalikan Light yang benar-benar baru, dan light = light.next(); menggantikan yang lama. Itulah sebabnya light dideklarasikan dengan mut. #[derive(Debug)] bekerja pada enum dengan cara yang sama seperti pada struct, jadi {:?} mencetak nama variannya (Red, Green, dan seterusnya, persis seperti yang ditulis di kode).
Menaruh logika di dalam method membuatnya tetap dekat dengan tipenya. Siapa pun yang memakai Light bisa memanggil light.seconds() tanpa perlu tahu bagaimana durasinya ditentukan. Dan kalau nanti kamu menambah varian baru, misalnya FlashingYellow, compiler akan menunjuk setiap match yang perlu kamu perbarui.
Option: enum yang sudah kamu pakai dari tadi
Section titled “Option: enum yang sudah kamu pakai dari tadi”Tipe Option, yang akan kamu pelajari dengan lengkap di halamannya sendiri, hanyalah sebuah enum yang didefinisikan di standard library:
enum Option<T> { Some(T), None,}Begitu kamu paham enum, Option tidak lagi terlihat seperti sintaks khusus. Ia hanyalah enum biasa dengan dua varian, dan salah satunya kebetulan membawa sebuah nilai.
Kenapa tidak pakai struct dengan field “jenis” saja?
Section titled “Kenapa tidak pakai struct dengan field “jenis” saja?”Kamu bisa saja meniru ini dengan struct dan field string seperti "circle" atau "rectangle". Tetapi dengan cara itu, tidak ada yang mencegahmu salah ketik "circl", dan kamu harus mengecek sendiri field mana yang benar-benar relevan untuk setiap jenis. Enum membuat kombinasi yang tidak valid mustahil ditulis sejak awal, dan compiler memaksamu menangani setiap varian.
Latihan
Section titled “Latihan”1. Hitung koin
Section titled “1. Hitung koin”Buat enum Coin dengan varian Penny, Nickel, Dime, dan Quarter (koin Amerika senilai 1, 5, 10, dan 25 sen). Tulis fn value_in_cents(coin: &Coin) -> u32, lalu jumlahkan isi dompet berisi koin: satu quarter, satu dime, satu nickel, satu penny, dan satu quarter lagi. Petunjuk: simpan koin-koinnya di sebuah array dan lakukan perulangan pada &purse.
Lihat solusi
enum Coin { Penny, Nickel, Dime, Quarter,}
fn value_in_cents(coin: &Coin) -> u32 { match coin { Coin::Penny => 1, Coin::Nickel => 5, Coin::Dime => 10, Coin::Quarter => 25, }}
fn main() { let purse = [Coin::Quarter, Coin::Dime, Coin::Nickel, Coin::Penny, Coin::Quarter];
let mut total = 0; for coin in &purse { total += value_in_cents(coin); }
println!("Total: {total} sen");}Output:
Total: 66 sen2. Tambahkan segitiga
Section titled “2. Tambahkan segitiga”Ambil enum Shape dari halaman ini dan tambahkan varian Triangle yang menyimpan panjang ketiga sisinya. Lalu tulis fungsi perimeter (keliling, yaitu jarak mengelilingi bentuk itu) yang bekerja untuk setiap bentuk. Petunjuk: keliling lingkaran adalah 2.0 * 3.14159 * radius. Keliling segitiga adalah jumlah ketiga sisinya.
Lihat solusi
enum Shape { Circle(f64), Rectangle(f64, f64), Triangle(f64, f64, f64),}
fn perimeter(shape: &Shape) -> f64 { match shape { Shape::Circle(radius) => 2.0 * 3.14159 * radius, Shape::Rectangle(width, height) => 2.0 * (width + height), Shape::Triangle(a, b, c) => a + b + c, }}
fn main() { let shapes = [ Shape::Circle(1.0), Shape::Rectangle(4.0, 5.0), Shape::Triangle(3.0, 4.0, 5.0), ];
for shape in &shapes { println!("{:.2}", perimeter(shape)); }}Output:
6.2818.0012.00Coba hapus cabang Shape::Triangle dari match. Rust menolak meng-compile, karena match-nya tidak lagi mencakup semua varian. Begitulah cara compiler memastikan kamu tidak pernah melupakan satu kasus pun.
3. Suhu
Section titled “3. Suhu”Buat enum Temperature dengan dua varian yang masing-masing menyimpan sebuah f64: Celsius dan Fahrenheit. Tambahkan method to_celsius(&self) -> f64, lalu cetak Celsius(25.0), Fahrenheit(212.0), dan Fahrenheit(50.0) dalam °C dengan satu angka di belakang koma. Petunjuk: untuk mengonversi, pakai (f - 32.0) * 5.0 / 9.0.
Lihat solusi
enum Temperature { Celsius(f64), Fahrenheit(f64),}
impl Temperature { fn to_celsius(&self) -> f64 { match self { Temperature::Celsius(c) => *c, Temperature::Fahrenheit(f) => (f - 32.0) * 5.0 / 9.0, } }}
fn main() { let readings = [ Temperature::Celsius(25.0), Temperature::Fahrenheit(212.0), Temperature::Fahrenheit(50.0), ];
for t in &readings { println!("{:.1} °C", t.to_celsius()); }}Output:
25.0 °C100.0 °C10.0 °CKarena self dipinjam, c dan f adalah referensi (&f64). Operasi matematika bisa langsung dilakukan pada referensi, tetapi untuk mengembalikan nilai Celsius biasa kamu menulis *c, yang artinya “angka yang ditunjuk oleh c”.
Ringkasan
Section titled “Ringkasan”- Enum mendefinisikan tipe sebagai salah satu dari beberapa varian bernama:
enum Direction { North, South, ... }. - Varian bisa membawa datanya sendiri, dan varian yang berbeda bisa membawa data yang berbeda pula.
matchdengan mudah membongkar data di dalam enum, dan mewajibkan setiap varian ditangani.- Enum bisa punya method di dalam blok
impl, sama seperti struct. Option(danResult, yang dibahas nanti) hanyalah enum dari standard library, tidak ada yang ajaib.
Kuis singkat
Section titled “Kuis singkat”Kuis singkat
1. Apa yang bisa dilakukan setiap varian enum di Rust, tidak seperti enum biasa di bahasa lain?
2. Kenapa contoh match pada Direction tidak butuh cabang _ untuk menangkap sisanya?
3. Sebenarnya Option<T> diimplementasikan sebagai apa di standard library?
Skor: 0 / 3
Bacaan lebih lanjut
Section titled “Bacaan lebih lanjut”- Defining an Enum — The Rust Book (bahasa Inggris): varian yang membawa data, method pada enum, dan kenapa Rust memakai
Option, bukan null. - The
matchControl Flow Construct — The Rust Book (bahasa Inggris): pembahasan mendalam tentang match pada enum, dengan contoh memilah koin dan pola penangkap sisa. - Enums — Rust by Example (bahasa Inggris): contoh yang bisa dijalankan, termasuk varian dengan field bernama seperti
Click { x: i64, y: i64 }. - Enumerations — The Rust Reference (bahasa Inggris): aturan lengkapnya, seperti memberi varian nilai angka secara eksplisit.
Option— dokumentasi standard library (bahasa Inggris): definisi asli enum yang paling sering akan kamu pakai, beserta semua method pembantunya.