A.26. Enum
Enum atau enumerated type adalah sebuah tipe data yang digunakan untuk menampung nilai konstan. Pada chapter ini kita akan mempelajarinya.
Enum ada sedikit kemiripan dengan konstanta, bedanya ada pada nilai atau underlying value-nya. Jika di konstanta, yang didefinisikan adalah nama beserta value-nya, di enum yang didefinisikan adalah tipe data enum dan enum value. Enum value ini bentuknya seperti variabel tanpa nilai (lebih tepatnya nama dari enum value tersebut adalah nilainya). Lebih jelasnya silakan ikut pembahasan chapter ini.
A.26.1. Keyword enum
Keyword enum
digunakan untuk membuat enumerated type. Cara penulisannya seperti berikut:
enum NamaEnum {
NilaiEnum1,
Nilai2,
NilaiEnumKe3,
// ...
}
NamaEnum
di atas adalah tipe data custom yang didefinisikan bertipe enum. Sedangkan NilaiEnum1
, Nilai2
, dan NilaiEnumKe3
adalah yang disebut dengan enum value. Dengan itu maka ketiga enum values tersebut tipe datanya adalah sama, yaitu NamaEnum
.
Mari kita lanjut praktik. Berikut ini adalah definisi konstanta yang menggunakan tipe data string untuk menampung nilai konstan-nya. Lalu di bawahnya ada lagi definisi nilai konstan tetapi menggunakan enum sebagai tipe data yang digunakan.
// definisi konstanta
const SuperheroSuperman: &str = "superman";
const SuperheroOmniMan: &str = "omniman";
const SuperheroHomelander: &str = "homelander";
const SuperheroHyperion: &str = "hyperion";
// definisi enum
enum Superhero {
Superman,
OmniMan,
Homelander,
Hyperion,
};
Di contoh bisa dilihat, Superhero
adalah tipe data enum baru. Dari tipe data tersebut dibuat 4 buah enum values, yaitu Superman
, OmniMan
, Homelander
, dan Hyperion
.
Pada pembuatan konstanta, tipe data beserta value-nya harus ditentukan di awal. Pada enum, yang perlu didefinisikan adalah tipe data enum-nya (sebagai contoh Superhero
) kemudian diikut dengan enum value yang dituliskan tanpa pengisian nilai.
Definisi variabel dengan isi konstanta:
let value1: &str = SuperheroSuperman;
let value2 = SuperheroOmniMan;
// ...Definisi variabel bertipe data enum
Superhero
:let value3: Superhero = Superhero::Superman;
let value4 = Superhero::OmniMan;
// ...
Notasi path digunakan dalam penulisan enum value dengan format NamaEnum::EnumValue
A.26.2. Naming convention enum
Sesuai anjuran di halaman dokumentasi Rust, upper camel case digunakan dalam penamaan Enum beserta value-nya.
Contoh:
enum Superhero {
Superman,
OmniMan,
Homelander,
Hyperion,
};
A.26.3. Seleksi kondisi enum
Tipe data enum biasa dipakai pada seleksi kondisi, namun caranya sedikit berbeda. Default-nya keyword if
tidak bisa digunakan pada tipe data enum.
Pada contoh berikut, statement seleksi kondisi value3
menghasilkan error:
// seleksi kondisi pada konstanta
if value1 == SuperheroSuperman {
println!("hello superman!");
}
// seleksi kondisi pada enum
if value3 == Superhero::Superman {
println!("hello superman!");
}
Error tersebut muncul karena tipe data enum
Superhero
tidak memiliki traitPartialEq
. Lebih jelasnya mengenai trait dibahas pada chapter Traits.
Lalu bagaimana cara pengaplikasian seleksi kondisi pada tipe enum? Ada beberapa cara, namun yang paling praktis adalah keyword match
.
A.26.4. Keyword match
match
adalah salah satu keyword untuk operasi seleksi kondisi di Rust. Penerapan keyword ini cukup luas, namun pada chapter ini hanya akan dibahas penerapannya yang relevan dengan topik enum.
Mari kita pelajarinya sembari praktik. Silakan buat package baru, lalu definisikan tipe enum Food
berikut beserta 4 enum value-nya.
enum Food {
PenyetanTerangBulan,
PizzaNanas,
EsKrimIkanMujaer,
MiGorengKuah,
}
Lalu buat sebuah variabel bernama makanan_favorit
untuk menampung salah satu nilai enum. Kemudian gunakan keyword match
untuk menerapkan operasi seleksi kondisi dengan aksi menampilkan sebuah pesan sesuai dengan nilai yang cocok.
fn main() {
let makanan_favorit: Food = Food::PenyetanTerangBulan;
match makanan_favorit {
Food::PenyetanTerangBulan => {
println!("your food taste is quite ... unique");
},
Food::PizzaNanas => {
println!("it's morally wrong to have pineaple on top of pizza");
},
Food::EsKrimIkanMujaer => {
println!("I don't know what to say");
},
Food::MiGorengKuah => {
println!("sometimes people do eat this, but it's ok");
}
}
}
Di atas bisa dilihat bagaimana cara penggunaan keyword match
untuk penerapan seleksi kondisi pada tipe data enum.
Notasi penulisannya kurang lebih seperti ini:
match variabel_enum {
TipeEnum::ValueEnum1 => {
// ...
},
TipeEnum::ValueEnum2 => {
// ...
},
// ...
}
Kembali ke contoh program, variabel makanan_favorit
dicek nilainya menggunakan keyword match
.
Jika nilainya adalah
Food::PenyetanTerangBulan
, muncul pesan:"your food taste is quite ... unique"
Jika nilainya adalah
Food::PizzaNanas
, muncul pesan:"it is morally wrong to have pineaple on top of pizza"
Jika nilainya adalah
Food::EsKrimIkanMujaer
, muncul pesan:"I don't know what to say"
Jika nilainya adalah
Food::MiGorengKuah
, muncul pesan:"sometimes people do eat this, but it's ok"
Jalankan program untuk melihat hasilnya:
Keyword match
ini sebenarnya tidak hanya digunakan untuk seleksi kondisi saja. Di Rust ada yang disebut dengan pattern matching. Metode pattern matching ini memahami special syntax yang kegunaanya lebih luas dibanding hanya sekedar seleksi kondisi biasa.
Lebih jelasnya mengenai pattern matching dibahas pada chapter Pattern Matching
A.26.5. Enum value ➜ tuple struct-like
Enum value di struct bisa juga didesain seperti tuple struct. Sebagai contoh, enum Food
di atas akan kita tambahi dengan satu enum value baru berbentuk tuple struct.
Silakan tambahkan enum value MakananLainnya
berikut. Enum ini kita fungsikan untuk mengidentifikasi data makanan lainnya selain dari yang sudah ada di enum Food
. Notasi penulisan tuple struct MakananLainnya(String)
artinya enum value MakananLainnya
didefinisikan untuk bisa menampung data property dalam bentuk String
.
enum Food {
PenyetanTerangBulan,
PizzaNanas,
EsKrimIkanMujaer,
MiGorengKuah,
MakananLainnya(String), // <---- enum value baru
}
Sekarang ubah isi variabel makanan_favorit
dengan enum value baru yang sudah dibuat. Syntax Food::MakananLainnya(nasi_goreng)
artinya enum value yang digunakan adalah Food::MakananLainnya
dengan isi property didapat dari variabel nasi_goreng
.
Tambahkan juga Food::MakananLainnya
dalam blok kode match
.
fn main() {
// enum value MakananLainnya digunakan
// dengan isi property adalah string "nasi goreng"
let nasi_goreng = String::from("nasi goreng");
let makanan_favorit = Food::MakananLainnya(nasi_goreng);
match makanan_favorit {
Food::PenyetanTerangBulan => {
println!("your food taste is quite ... unique");
},
Food::PizzaNanas => {
println!("it's morally wrong to have pineaple on top of pizza");
},
Food::EsKrimIkanMujaer => {
println!("I don't know what to say. this should be illegal");
},
Food::MiGorengKuah => {
println!("sometimes people do eat this, but it's ok");
},
Food::MakananLainnya(m) => { // <---- seleksi kondisi baru
println!("do you like {m}? nice taste!");
}
}
}
Bisa dilihat ada keunikan dalam penulisan seleksi kondisi Food::MakananLainnya
dalam blok kode match
. Di situ ada parameter bernama m
yang parameter tersebut akan berisi data property jika memang match dengan makanan_favorit
.
Coba jalankan untuk melihat hasilnya:
O iya, jumlah property value enum berbentuk tuple struct ini tidak terbatas ya. Pada contoh di atas, Food::MakananLainnya
hanya memiliki 1 property. Lebih dari satu juga bisa.
A.26.6. Enum value ➜ struct-like
Enum value bisa juga didesain memiliki property seperti struct.
Mari kita terapkan pada kode sebelumnya. Tambahkan 1 buah enum value lagi dengan nama MieSetan
yang ditulis dalam bentuk struct-like, dan memiliki 2 buah property.
enum Food {
PenyetanTerangBulan,
PizzaNanas,
EsKrimIkanMujaer,
MiGorengKuah,
MakananLainnya(String),
MieSetan { level_pedas: i32, pakek_piring: bool }
}
Setelah itu, isi variabel dengan nilai adalah enum value Food::MieSetan
, level pedasnya 5, dan tanpa piring.
Tak lupa tambahkan seleksi kondisi untuk Food::MieSetan
pada blok kode match
.
fn main() {
let makanan_favorit = Food::MieSetan {
level_pedas: 5,
pakek_piring: false
};
match makanan_favorit {
Food::PenyetanTerangBulan => {
println!("your food taste is quite ... unique");
},
Food::PizzaNanas => {
println!("it's morally wrong to have pineaple on top of pizza");
},
Food::EsKrimIkanMujaer => {
println!("I don't know what to say. this should be illegal");
},
Food::MiGorengKuah => {
println!("sometimes people do eat this, but it's ok");
},
Food::MakananLainnya(m) => {
println!("do you like {m}? nice taste!");
},
Food::MieSetan { level_pedas, pakek_piring } => {
if level_pedas > 3 {
println!("mie setan lvl {} is too much!", level_pedas);
} else {
println!("mie setan lvl {} is perfect!", level_pedas);
}
if !pakek_piring {
println!("how are you going to eat the food without a plate, huh?");
}
}
}
}
Dalam seleksi kondisi Food::MieSetan
bisa dilihat ada beberapa statement. Kurang lebih jika nilai dari variabel makanan_favorit
adalah Food::MieSetan
maka:
- Akan memunculkan pesan yang berbeda tergantung level pedasnya
- Dan jika terdeteksi tidak menggunakan piring, dimunculkan pesan tambahan
A.26.7. Aturan pattern matching enum
Dalam blok kode match
, semua enum value harus dituliskan. Jika tidak, pasti muncul error. Contohnya bisa dilihat di gambar berikut, beberapa seleksi kondisi enum value di-remark, hasilnya ada error.
Error tersebut sebenarnya bisa diantisipasi dengan menambahkan seleksi kondisi dengan penulisan seperti berikut:
match makanan_favorit {
Food::PenyetanTerangBulan => {
println!("your food taste is quite ... unique");
},
Food::PizzaNanas => {
println!("it's morally wrong to have pineaple on top of pizza");
},
_ => {
println!("never heard about that food");
}
}
Menggunakan blok kode match
di atas, jika nilai makanan_favorit
adalah selain Food::PenyetanTerangBulan
dan Food::PizzaNanas
, maka pesan never heard about that food
adalah yang muncul di layar.
Selain variabel _
bisa juga menggunakan nama variabel apapun, misalnya some_var
. Namun jika variabel tersebut tidak digunakan dalam blok kode, akan muncul warning.
A.26.8. Enum module & visibility
Mari kita coba cek perihal visibility dari enum. Siapkan package baru dengan struktur seperti berikut:
my_package
│─── Cargo.toml
└─── src
│─── constants.rs
└─── main.rs
Pada file constants.rs
, tambahkan enum Company
berikut. Pastikan enum adalah publik dengan menambahkan keyword pub
pada deklarasinya.
pub enum Company {
Apple,
Microsoft,
Google,
Github
}
Kemudian tambahkan kode berikut di main.rs
.
mod constants;
fn main() {
let company = constants::Company::Apple;
match company {
constants::Company::Apple => {
print!("apple")
},
_ => {
print!("other than apple")
}
}
}
Jalankan, hasilnya tidak error, karena Company
didefinisikan publik.
Coba lakukan modifikasi dengan menghilangkan keyword pub
saat definisi enum, hasilnya pasti error.
Pada tipe data enum, keyword
pub
cukup ditambahkan pada definisi enum type, tidak perlu ditambahkan satu persatu di tiap enum values.
A.26.9. Generic pada enum
Pembahasan mengenai generic pada enum ada pada chapter Generics.
A.26.10. Pembahasan lanjutan pattern matching
Pembahasan yang lebih mendetail tentang keyword match
dan pattern matching ada pada chapter Pattern Matching.
Catatan chapter 📑
◉ Source code praktik
github.com/novalagung/dasarpemrogramanrust-example/../enum
◉ Work in progress
- Pembahasan tentang associated function dan method pada enum