Enums
See this chapter on YouTube: Part 1, Part 2, Part 3 and Part 4
enum
adalah kependekan dari enumerations. Ia terlihat mirip seperti struct, tapi sebenarnya berbeda. Inilah perbedaannya:
- Gunakan
struct
apabila Anda menginginkan satu hal DAN juga hal lainnya. - Gunakan
enum
apabila Anda memilih satu hal ATAU hal yang lainnya.
Jadi, struct digunakan untuk menyimpan banyak hal secara bersamaan, sedangkan enum digunakan untuk memilih banyak pilihan secara bersamaan.
Untuk mendeklarasikan enum, tulis enum
dan gunakan code block beserta pilihan-pilihan di dalamnya, dan pisahkan pilihan-pilihan tersebut menggunakan koma. Sama seperti struct
, bagian akhir dari enum boleh memiliki koma atau tidak. Kita akan membuat enum dengan nama ThingsInTheSky
:
enum ThingsInTheSky { Sun, Stars, } fn main() {}
Ini adalah enum karena Anda hanya bisa melihat salah satu, Sun
(matahari di siang hari), atau Stars
(bintang di malam hari): Anda harus memilih salah satunya. Pilihan-pilihan ini biasa disebut sebagai variants.
// membuat enum dengan 2 pilihan enum ThingsInTheSky { Sun, Stars, } // Dengan function ini, kita bisa menggunakan i32 untuk membuat ThingsInTheSky. fn create_skystate(time: i32) -> ThingsInTheSky { match time { 6..=18 => ThingsInTheSky::Sun, // Antara jam 6 dan 18, kita bisa melihat Matahari _ => ThingsInTheSky::Stars, // Sebaliknya, kita bisa melihat bintang } } // Dengan function ini, kita bisa menggunakan match untuk melakukan sesuatu terhadap 2 pilihan yang ada pada ThingsInTheSky. fn check_skystate(state: &ThingsInTheSky) { match state { ThingsInTheSky::Sun => println!("I can see the sun!"), ThingsInTheSky::Stars => println!("I can see the stars!") } } fn main() { let time = 8; // jam 8 pagi let skystate = create_skystate(time); // create_skystate akan me-return ThingsInTheSky check_skystate(&skystate); // Berikan reference dari skystate, sehingga function check_skystate bisa membaca isi dari variabel skystate }
Dan hasilnya adalah I can see the sun!
.
Anda juga bisa menambahkan data pada enum.
enum ThingsInTheSky { Sun(String), // Sekarang setiap variant memiliki sebuah string Stars(String), } fn create_skystate(time: i32) -> ThingsInTheSky { match time { 6..=18 => ThingsInTheSky::Sun(String::from("I can see the sun!")), // Tuliskan stringnya disini _ => ThingsInTheSky::Stars(String::from("I can see the stars!")), } } fn check_skystate(state: &ThingsInTheSky) { match state { ThingsInTheSky::Sun(description) => println!("{}", description), // Memberikan string sebuah nama deskripsi, sehingga kita bisa menggunakannya ThingsInTheSky::Stars(n) => println!("{}", n), // Atau Anda juga bisa menamakannya dengan n. Atau dengan nama lain semau Anda - itu tidak masalah } } fn main() { let time = 8; // jam 8 pagi let skystate = create_skystate(time); // create_skystate akan me-return ThingsInTheSky check_skystate(&skystate); // Berikan reference dari skystate, sehingga function check_skystate bisa membaca isi dari variabel skystate }
Program di atas akan mencetak hal yang sama: I can see the sun!
Anda bisa juga melakukan "import" kepada enum, sehingga Anda tidak perlu mengetik terlalu banyak. Ini adalah contoh dimana kita harus menuliskan Mood::
setiap kita menuliskan variant dari enum Mood
di dalam sebuah match:
enum Mood { Happy, Sleepy, NotBad, Angry, } fn match_mood(mood: &Mood) -> i32 { let happiness_level = match mood { Mood::Happy => 10, // Kita harus menuliskan Mood:: setiap saat Mood::Sleepy => 6, Mood::NotBad => 7, Mood::Angry => 2, }; happiness_level } fn main() { let my_mood = Mood::NotBad; let happiness_level = match_mood(&my_mood); println!("Out of 1 to 10, my happiness is {}", happiness_level); }
Hasil cetaknya adalah Out of 1 to 10, my happiness is 7
. Saatnya kita gunakan import
sehingga kita bisa mengetik lebih sedikit. Untuk meng-import semua yang ada di enum, tuliskan *
. Catatan: meskipun ini *
adalah simbol yang sama dengan yang digunakan dereferencing, tapi ini benar-benar berbeda.
enum Mood { Happy, Sleepy, NotBad, Angry, } fn match_mood(mood: &Mood) -> i32 { use Mood::*; // Kita meng-import semua yang ada di dalam Mood. Sekarang kita cukup menuliskan Happy, Sleepy, dsb. let happiness_level = match mood { Happy => 10, // Kita tidak perlu lagi menuliskan Mood:: Sleepy => 6, NotBad => 7, Angry => 2, }; happiness_level } fn main() { let my_mood = Mood::Happy; let happiness_level = match_mood(&my_mood); println!("Out of 1 to 10, my happiness is {}", happiness_level); }
enum
juga bisa direpresentasikan sebagai integer. Ini dikarenakan Rust memberikan angka pada setiap variant di enum
yang dimulai dengan 0. Anda bisa melakukan hal ini jika enum yang Anda buat tidak memiliki type apapun didalamnya.
enum Season { Spring, // Jika Anda menuliskan Spring(String) atau suatu type yang lainnya, maka ia tidak akan berjalan Summer, Autumn, Winter, } fn main() { use Season::*; let four_seasons = vec![Spring, Summer, Autumn, Winter]; for season in four_seasons { println!("{}", season as u32); } }
Berikut adalah hasilnya:
0
1
2
3
Anda juga bisa merepresentasikannya menggunakan angka yang lain, jika Anda menginginkannya. Rust tidak begitu peduli dalam hal ini dan Anda bisa menampilkannya dengan cara yang sama. Cukup tambahakan =
, juga angka yang Anda inginkan ke variant yang ingin Anda representasikan dengan angka. Anda tidak perlu merepresentasikan semua variant tersebut dengan angka. Namun jika Anda tidak menuliskan representasi integernya secara manual, Rust akan mengambil nilai representasi integer dari variant yang sebelumnya, menambahkannya dengan 1, dan hasilnya dijadikan representasi pada variant yang tidak direpresentasikan secara manual tersebut.
enum Star { BrownDwarf = 10, RedDwarf = 50, YellowStar = 100, RedGiant = 1000, DeadStar, // Coba pikirkan hal ini. Angka berapa yang menjadi representasi integernya? } fn main() { use Star::*; let starvec = vec![BrownDwarf, RedDwarf, YellowStar, RedGiant]; for star in starvec { match star as u32 { size if size <= 80 => println!("Not the biggest star."), // Ingat: "size" tidaklah berarti apapun. Ia hanya sebuah nama yang kita pilih, sehingga kita bisa mencetaknya size if size >= 80 => println!("This is a good-sized star."), _ => println!("That star is pretty big!"), } } println!("What about DeadStar? It's the number {}.", DeadStar as u32); }
Berikut adalah hasilnya:
Not the biggest star.
Not the biggest star.
This is a good-sized star.
This is a good-sized star.
What about DeadStar? It's the number 1001.
DeadStar
semestinya direpresentasikan dengan angka 4, tapi sekarang ia berangka 1001.
Enums to use multiple types
Kita mengetahui bahwa item di dalam Vec
, array, dan yang lainnya, diisi dengan type yang sama (hanya tuples yang berbeda). Tapi sebenarnya Anda bisa menggunakan enum untuk dimasukkan type yang beragam. Bayangkan kita ingin memiliki sebuah Vec
dengan type u32
atau i32
. Tentu saja, Anda bisa membuat Vec<(u32, i32)>
(vec dengan tuple (u32, i32)
) namun kita hanya menginginkan salah satunya saja. Jadi disini Anda bisa menggunakan enum. Begini contohnya:
enum Number { U32(u32), I32(i32), } fn main() {}
Jadi pada enum tersebut terdapat 2 variant: variant U32
degnan type u32
di dalamnya, dan I32
dengan variant i32
di dalamnya. U32
dan I32
hanyalah sebuah nama yang kita buat. Ia bisa saja bernama UThirtyTwo
atau IThirtyTwo
atau apapun itu.
Sekarang, jika kita masukkan enum Number
tersebut ke dalam Vec
kita akan memiliki Vec<Number>
, dan compiler akan menjalankannya dengan aman karena typenya sama semua (semuanya menggunakan type Number
). Compiler tidak peduli bahwa kita memiliki u32
ataupun i32
karena kedua type tersebut dibungkus di dalam sebuah type bernama Number
. Dan karena Number
adalah sebuah enum, Anda mesti memilih salah satunya, yang tentunya adalah type yang kita inginkan. Kita akan menggunakan method .is_positive()
untuk menentukan typenya. Jika true
Maka kita akan pilih U32
, dan jika false
maka kita akan memilih I32
.
Codenya adalah sebagai berikut:
enum Number { U32(u32), I32(i32), } fn get_number(input: i32) -> Number { let number = match input.is_positive() { true => Number::U32(input as u32), // ganti typenya menjadi u32 jika ia positive false => Number::I32(input), // sebaliknya, cukup masukkan angkanya saja karena secara default typenya adalah i32 }; number } fn main() { let my_vec = vec![get_number(-800), get_number(8)]; for item in my_vec { match item { Number::U32(number) => println!("It's a u32 with the value {}", number), Number::I32(number) => println!("It's an i32 with the value {}", number), } } }
Dan, voila! Hasilnya sesuai dengan yang kita inginkan:
It's an i32 with the value -800
It's a u32 with the value 8