Other collections
Rust memiliki beberapa jenis collection. Kita bisa melihatnya di https://doc.rust-lang.org/beta/std/collections/ pada standard library. Laman tersebut dengan baik menjelaskan tentang mengapa kita menggunakan suatu jenis collection, jadi pergilah ke laman tersebut jika Anda tidak tahu type apa yang Anda inginkan. Collections ini semuanya berada di dalam std::collections
pada standard library. Cara terbaik untuk menggunkannya adalah dengan menggunakan statement use
, seperti yang kita lakukan pada enums
yang kita buat sebelumnya. Kita akan mulai dengan HashMap
, yang mana ia adalah collection yang paling umum.
HashMap (and BTreeMap)
HashMap adalah collection yang terbuat dari keys dan values. Anda menggunakan key untuk melihat value yang cocok dengan keynya. Anda bisa membuat sebuah HashMap
yang baru dengan menggunakan HashMap::new()
dan juga .insert(key, value)
untuk memasukkan item ke dalamnya.
HashMap
tidaklah berurutan, sehingga jika Anda mencetak setiap key yang berada pada HashMap
secara bersamaan, ia akan tercetak dengan urutan yang berbeda-beda. Kita bisa melihat hal tersebut pada contoh di bawah ini:
use std::collections::HashMap; // Ini ditulis sehingga kita bisa menuliskannya hanya dengan "HashMap" daripada menuliskan std::collections::HashMap setiap saat struct City { name: String, population: HashMap<u32, u32>, // population akan memiliki tahun dan jumlah populasi pada tahun tersebut } fn main() { let mut tallinn = City { name: "Tallinn".to_string(), population: HashMap::new(), // Sejauh ini HashMap masih kosong }; tallinn.population.insert(1372, 3_250); // masukkan 3 data tallinn.population.insert(1851, 24_000); tallinn.population.insert(2020, 437_619); for (year, population) in tallinn.population { // HashMapnya bertype HashMap<u32, u32> sehingga ia akan akan mengembalikan dua item setiap saat println!("In the year {} the city of {} had a population of {}.", year, tallinn.name, population); } }
Hasil cetaknya adalah:
In the year 1372 the city of Tallinn had a population of 3250.
In the year 2020 the city of Tallinn had a population of 437619.
In the year 1851 the city of Tallinn had a population of 24000.
Atau bisa juga seperti ini:
In the year 1851 the city of Tallinn had a population of 24000.
In the year 2020 the city of Tallinn had a population of 437619.
In the year 1372 the city of Tallinn had a population of 3250.
Anda bisa melihat bahwa ia ditampilkan secara tidak berurutan dan sering berubah-ubah.
Jika Anda ingin HashMap
yang berurutan, Anda bisa menggunakan BTreeMap
. Sebenarnya mereka itu mirip satu sama lain, sehingga kita bisa dengan cepat mengubah HashMap
kita ke BTreeMap
. Anda bisa melihat code yang dituliskan pun benar-benar hampir sama.
use std::collections::BTreeMap; // Cukup ubah HashMap ke BTreeMap struct City { name: String, population: BTreeMap<u32, u32>, // Cukup ubah HashMap ke BTreeMap } fn main() { let mut tallinn = City { name: "Tallinn".to_string(), population: BTreeMap::new(), // Cukup ubah HashMap ke BTreeMap }; tallinn.population.insert(1372, 3_250); tallinn.population.insert(1851, 24_000); tallinn.population.insert(2020, 437_619); for (year, population) in tallinn.population { println!("In the year {} the city of {} had a population of {}.", year, tallinn.name, population); } }
Dan ia akan selalu mencetak:
In the year 1372 the city of Tallinn had a population of 3250.
In the year 1851 the city of Tallinn had a population of 24000.
In the year 2020 the city of Tallinn had a population of 437619.
Sekarang kita kembali lagi ke HashMap
.
Anda bisa mengambil value di dalam HashMap
cukup dengan menuliskan keynya di dalam []
square brackets. Pada contoh selanjutnya kita akan memberikan value pada key Bielefeld
, yang mana valuenya adalah Germany
. Namun berhati-hatilah, karena programnya akan crash jika keynya tidak ditemukan. Sebagai contoh, jika Anda menulis println!("{:?}", city_hashmap["Bielefeldd"]);
maka ia akan crash, karena key Bielefeldd
tidak ditemukan.
Jika Anda tidak yakin apakah keynya ada atau tidak, Anda bisa menggunakan .get()
yang mana akan mengembalikan Option
. Jika keynya ada, ia akan mengembalikan Some(value)
. Dan jika tidak, maka ia akan mengembalikan None
alih-alih membuat programnya menjadi crash. Itulah mengapa .get()
adalah cara teraman untuk mendapatkan value dari HashMap
.
use std::collections::HashMap; fn main() { let canadian_cities = vec!["Calgary", "Vancouver", "Gimli"]; let german_cities = vec!["Karlsruhe", "Bad Doberan", "Bielefeld"]; let mut city_hashmap = HashMap::new(); for city in canadian_cities { city_hashmap.insert(city, "Canada"); } for city in german_cities { city_hashmap.insert(city, "Germany"); } println!("{:?}", city_hashmap["Bielefeld"]); println!("{:?}", city_hashmap.get("Bielefeld")); println!("{:?}", city_hashmap.get("Bielefeldd")); }
Hasilnya adalah:
"Germany"
Some("Germany")
None
Ini dikarenakan ada kunci yang bernama Bielefeld, namun kunci bernama Bielefeldd tidak ditemukan.
Jika HashMap
telah memiliki key di saat Anda mencoba untuk memasukkan Hashmap value yang baru, maka ia akan meng-overwrite valuenya:
use std::collections::HashMap; fn main() { let mut book_hashmap = HashMap::new(); book_hashmap.insert(1, "L'Allemagne Moderne"); book_hashmap.insert(1, "Le Petit Prince"); book_hashmap.insert(1, "μλμ° μ€λΈ μ μ΄ μ€λ§μΌ"); book_hashmap.insert(1, "Eye of the World"); println!("{:?}", book_hashmap.get(&1)); }
Ia akan mencetak Some("Eye of the World")
, karena value tersebut adalah value yang terakhir dimasukkan menggunakan .insert()
.
Adalah hal yang mudah untuk memeriksa apakah sebuah entry exist atau tidak, karena Anda bisa memeriksanya dengan menggunakan .get()
yang mana ia akan memberikan kita Option
:
use std::collections::HashMap; fn main() { let mut book_hashmap = HashMap::new(); book_hashmap.insert(1, "L'Allemagne Moderne"); if book_hashmap.get(&1).is_none() { // is_none() returns a bool: true if it's None, false if it's Some book_hashmap.insert(1, "Le Petit Prince"); } println!("{:?}", book_hashmap.get(&1)); }
Ia akan mencetak Some("L\'Allemagne Moderne")
karena kita telah memiliki key 1
, sehingga kita tidak perlu untuk memasukkan memasukkan Le Petit Prince
.
HashMap
memiliki method yang menarik, namanya .entry()
, yang mana pastinya membuat Anda ingin mencobanya. Dengan method ini, Anda bisa mencoba untuk membuat sebuah entry dan menggunakan method lain seperti .or_insert()
untuk memasukkan value jika tidak ada keynya. Bagian menariknya adalah ia juga akan memberikan mutable reference sehingga Anda bisa mengubahnya jika Anda menginginkannya. Ini adalah contoh pertama dimana kita memasukkan true
setiap kita memasukkan judul buku ke dalam HashMap
.
Anggaplah kita memiliki perpustakaan dan kita ingin memantau buku-buku yang kita miliki.
use std::collections::HashMap; fn main() { let book_collection = vec!["L'Allemagne Moderne", "Le Petit Prince", "Eye of the World", "Eye of the World"]; // Eye of the World muncul dua kali let mut book_hashmap = HashMap::new(); for book in book_collection { book_hashmap.entry(book).or_insert(true); } for (book, true_or_false) in book_hashmap { println!("Do we have {}? {}", book, true_or_false); } }
Hasilnya adalah:
Do we have Eye of the World? true
Do we have Le Petit Prince? true
Do we have L'Allemagne Moderne? true
Namun hasil tersebut tentunya bukanlah yang kita inginkan. Mungkin akan lebih baik jika jumlah bukunya juga dihitung sehingga kita tahu bahwa kita memiliki 2 copy dari Eye of the World. Pertama-tama, kita lihat terlebih dahulu apa yang .entry()
lakukan, dan apa yang .or_insert()
lakukan. .entry()
sebenarnya mengembalikan sebuah enum
yang disebut dengan Entry
:
#![allow(unused)] fn main() { pub fn entry(&mut self, key: K) -> Entry<K, V> // π§ }
Ini adalah laman yang menjelaskan tentang Entry. Dibawah ini adalah versi singkat dari codenya. K
adalah key dan V
adalah value.
#![allow(unused)] fn main() { // π§ use std::collections::hash_map::*; enum Entry<K, V> { Occupied(OccupiedEntry<K, V>), Vacant(VacantEntry<K, V>), } }
Kemudian di saat kita menggunakan method .or_insert()
, ia akan memeriksa enum yang dikembalikan dan menentukan apa yang harus dilakukan.
#![allow(unused)] fn main() { fn or_insert(self, default: V) -> &mut V { // π§ match self { Occupied(entry) => entry.into_mut(), Vacant(entry) => entry.insert(default), } } }
Bagian menariknya adalah ia me-return mut
reference: &mut V
. Yang berarti Anda bisa menggunakan let
untuk memasukkan valuenya ke variabel, dan mengubah valuenya yang tersimpan di dalam HashMap
. Jadinya, untuk setiap buku, kita akan memasukkan 0 jika memang belum ada entry apapun sebelumnya. Dan jika sebelumnya sudah ada satu entry, kita akan gunakan += 1
pada reference untuk menambahkan angkanya. Maka, sekarang ia akan terlihat seperti ini:
use std::collections::HashMap; fn main() { let book_collection = vec!["L'Allemagne Moderne", "Le Petit Prince", "Eye of the World", "Eye of the World"]; let mut book_hashmap = HashMap::new(); for book in book_collection { let return_value = book_hashmap.entry(book).or_insert(0); // return_value adalah mutable reference. Jika belum ada entrynya, maka valuenya 0 *return_value +=1; // Sekarang, return_value setidaknya bernilau 1. Dan apabila jika sebelumnya entry bukunya sudah ada, maka jumlahnya akan bertambah 1 } for (book, number) in book_hashmap { println!("{}, {}", book, number); } }
Bagian terpentingnya adalah let return_value = book_hashmap.entry(book).or_insert(0);
. Jika Anda tidak menggunakan let
, maka yang Anda dapatkan adalah book_hashmap.entry(book).or_insert(0)
. Tanpa let
, ia tidak akan bisa melakukan apapun: ia akan memasukkan 0, dan tidak ada variabel yang mengambil mutable referencenya yang bernilai 0. Sehingga kita lakukan bind pada value tersebut ke return_value
sehingga kita bisa menyimpan nilai 0 tersebut. Kemudian kita tambahkan nilainya dengan 1, yang mana memberikan kita nilai bahwa jumlah pada setiap buku di dalam HashMap
setidaknya bernilai 1. Kemudian ketika .entry()
melihat Eye of the World lagi, ia tidak akan meng-insert apapun, tapi ia akan memberikan kita mutable 1. Kemudian kita tambahkan valuenya sehingga menjadi 2, dan itu sebabnya hasil dari program tersebut adalah seperti berikut:
L'Allemagne Moderne, 1
Le Petit Prince, 1
Eye of the World, 2
Anda juga bisa melakukan hal lain dengan .or_insert()
seperti insert ke vec dan kemudian melakukan push ke dalam vec. Anggap saja kita bertanya kepada laki-laki dan perempuan tentang apa yang mereka pikirkan dari seorang politisi. Mereka akan memberi rating dari 0 sampai 10. Kemudian kita ingin memasukkan angka tersebut ke dalam satu tempat untuk melihat apakah si politisi ini lebih populer dikalangan laki-laki atau perempuan. Berikut codenya:
use std::collections::HashMap; fn main() { let data = vec![ // Ini adalah data mentah ("male", 9), ("female", 5), ("male", 0), ("female", 6), ("female", 5), ("male", 10), ]; // tuple dengan type (&str, i32) let mut survey_hash = HashMap::new(); for item in data { survey_hash.entry(item.0).or_insert(Vec::new()).push(item.1); // melakukan pushes item.1 (i32) ke dalam Vec } for (male_or_female, numbers) in survey_hash { println!("{:?}: {:?}", male_or_female, numbers); } }
Hasil cetaknya adalah:
"female", [5, 6, 5]
"male", [9, 0, 10]
Line terpenting pada code tersebut adalah: survey_hash.entry(item.0).or_insert(Vec::new()).push(item.1);
. Jika ia melihat "female", ia akan memeriksa untuk melihat apakah sudah ada key "female" di dalam HashMap
. Jika tidak, ia akan melakukan insert Vec::new()
, kemundian melakukan push angkanya ke dalam vec yang sudah dibuat. Apabila ia menemukan bahwa "female" sudah ada di dalam HashMap
, ia tidak akan membuat Vec baru, dan hanya akan melakuan push angka tersebut ke dalam Vec yang sudah ada.
HashSet and BTreeSet
HashSet
sebenarnya adalah HashMap
yang hanya memiliki key. Pada laman tentang HashSet bagian awalnya memberikan penjelasan seperti berikut:
A hash set implemented as a HashMap where the value is ().
Sehingga ia adalah HashMap
dengan keys, tanpa values.
Anda sering menggunakan HashSet
jika Anda hanya ingin tahu apakah sebuah key ada atau tidak.
Bayangkan Anda memiliki 100 angka random, dan setiap angkanya di antara 1 dan 100. Jika Anda melakukannya seperti code di bawah ini, beberapa angka mungkin akan muncul lebih dari sekali, sementara yang lainnya tidak muncul sama sekali. Jika Anda menaruhnya di dalam HashSet
maka Anda akan memiliki daftar semua nomor yang muncul.
use std::collections::HashSet; fn main() { let many_numbers = vec![ 94, 42, 59, 64, 32, 22, 38, 5, 59, 49, 15, 89, 74, 29, 14, 68, 82, 80, 56, 41, 36, 81, 66, 51, 58, 34, 59, 44, 19, 93, 28, 33, 18, 46, 61, 76, 14, 87, 84, 73, 71, 29, 94, 10, 35, 20, 35, 80, 8, 43, 79, 25, 60, 26, 11, 37, 94, 32, 90, 51, 11, 28, 76, 16, 63, 95, 13, 60, 59, 96, 95, 55, 92, 28, 3, 17, 91, 36, 20, 24, 0, 86, 82, 58, 93, 68, 54, 80, 56, 22, 67, 82, 58, 64, 80, 16, 61, 57, 14, 11]; let mut number_hashset = HashSet::new(); for number in many_numbers { number_hashset.insert(number); // Ia hanya akan mengambil angka yang unik, sehingga angka yang sama tidak dimasukkan lebih dari sekali } let hashset_length = number_hashset.len(); // Panjang dari number_hashset memberi tahu kita berapa banyak angka di dalam HashSet tersebut println!("There are {} unique numbers, so we are missing {}.", hashset_length, 100 - hashset_length); // Akan kita cari tahu angka berapa saja yang tidak terdaftar pada HashSet tersebut let mut missing_vec = vec![]; for number in 0..100 { if number_hashset.get(&number).is_none() { // Jika .get() mengembalikan None, missing_vec.push(number); } } print!("It does not contain: "); for number in missing_vec { print!("{} ", number); } }
Hasilnya adalah:
There are 66 unique numbers, so we are missing 34.
It does not contain: 1 2 4 6 7 9 12 21 23 27 30 31 39 40 45 47 48 50 52 53 62 65 69 70 72 75 77 78 83 85 88 97 98 99
BTreeSet
mirip dengan HashSet
sama seperti BTreeMap
yang mirip dengan HashMap
. Jika kita cetak setiap item di dalam HashSet
, kita tidak tahu bagaimana urutannya saat dicetak:
#![allow(unused)] fn main() { for entry in number_hashset { // π§ print!("{} ", entry); } }
Mungkin saja ia akan mencetak seperti ini: 67 28 42 25 95 59 87 11 5 81 64 34 8 15 13 86 10 89 63 93 49 41 46 57 60 29 17 22 74 43 32 38 36 76 71 18 14 84 61 16 35 90 56 54 91 19 94 44 3 0 68 80 51 92 24 20 82 26 58 33 55 96 37 66 79 73
. Tapi jika Anda melakukan print untuk kedua, ketiga, dan kesekian kalinya, ia hampir tidak pernah mencetaknya dengan urutan yang sama lagi.
Nah ini dia, tentu saja akan lebih mudah apabila HashSet
diubah menjadi BTreeSet
jika Anda merasa perlu untuk menampilkannya secara berurutan. Pada code kita, kita hanya perlu mengganti yang semula menggunakan HashSet
menjadi menggunakan BTreeSet
.
use std::collections::BTreeSet; // Ubah HashSet ke BTreeSet fn main() { let many_numbers = vec![ 94, 42, 59, 64, 32, 22, 38, 5, 59, 49, 15, 89, 74, 29, 14, 68, 82, 80, 56, 41, 36, 81, 66, 51, 58, 34, 59, 44, 19, 93, 28, 33, 18, 46, 61, 76, 14, 87, 84, 73, 71, 29, 94, 10, 35, 20, 35, 80, 8, 43, 79, 25, 60, 26, 11, 37, 94, 32, 90, 51, 11, 28, 76, 16, 63, 95, 13, 60, 59, 96, 95, 55, 92, 28, 3, 17, 91, 36, 20, 24, 0, 86, 82, 58, 93, 68, 54, 80, 56, 22, 67, 82, 58, 64, 80, 16, 61, 57, 14, 11]; let mut number_btreeset = BTreeSet::new(); // Ubah HashSet ke BTreeSet for number in many_numbers { number_btreeset.insert(number); } for entry in number_btreeset { print!("{} ", entry); } }
Sekarang ia akan mencetaknya secara berurutan: 0 3 5 8 10 11 13 14 15 16 17 18 19 20 22 24 25 26 28 29 32 33 34 35 36 37 38 41 42 43 44 46 49 51 54 55 56 57 58 59 60 61 63 64 66 67 68 71 73 74 76 79 80 81 82 84 86 87 89 90 91 92 93 94 95 96
.
BinaryHeap
BinaryHeap
adalah jenis collection yang menarik, karena sebagian besar tidak berurutan tetapi sedikit bagiannya berurutan. Ia menyimpan item terbesar di bagian depan, tetapi item yang lain ada dalam urutan apapun (terkadang acak, terkadang berurutan).
Kita akan menggunakan list yang berbeda sebagai contoh.
use std::collections::BinaryHeap; fn show_remainder(input: &BinaryHeap<i32>) -> Vec<i32> { // Function ini menunjukkan sisa di dalam BinaryHeap. Sebenarnya iterator // lebih cepat daripada function - kita akan mempelajari tentang ini nanti. let mut remainder_vec = vec![]; for number in input { remainder_vec.push(*number) } remainder_vec } fn main() { let many_numbers = vec![0, 5, 10, 15, 20, 25, 30]; // Vector angka ini berurutan let mut my_heap = BinaryHeap::new(); for number in many_numbers { my_heap.push(number); } while let Some(number) = my_heap.pop() { // .pop() akan me-return Some(number) jika masih ada angka yang masih bisa di pop, None jika tidak. Ia akan melakukan pop dari depan println!("Popped off {}. Remaining numbers are: {:?}", number, show_remainder(&my_heap)); } }
Hasilnya adalah:
Popped off 30. Remaining numbers are: [25, 15, 20, 0, 10, 5]
Popped off 25. Remaining numbers are: [20, 15, 5, 0, 10]
Popped off 20. Remaining numbers are: [15, 10, 5, 0]
Popped off 15. Remaining numbers are: [10, 0, 5]
Popped off 10. Remaining numbers are: [5, 0]
Popped off 5. Remaining numbers are: [0]
Popped off 0. Remaining numbers are: []
Anda bisa melihat bahwa angka pada index ke-0 selalu angka yang terbesar: 25, 20, 15, 10, 5, kemudian 0. Namun yang lainnya diurutkan secara berbeda.
Kasus yang cocok untuk menggunakan BinaryHeap
adalah untuk membuat sebuah To-do list dengan fitur skala prioritas. Kita akan menggunakan type BinaryHeap<(u8, &str)>
dimana u8
adalah angka prioritas yang menunjukkan seberapa pentingnya sebuah task untuk dikerjakan. &str
adalah deskripsi tentang task apa saja yang harus dilakukan.
use std::collections::BinaryHeap; fn main() { let mut jobs = BinaryHeap::new(); // Tambahkan task yang akan dikerjakan hari ini jobs.push((100, "Write back to email from the CEO")); jobs.push((80, "Finish the report today")); jobs.push((5, "Watch some YouTube")); jobs.push((70, "Tell your team members thanks for always working hard")); jobs.push((30, "Plan who to hire next for the team")); while let Some(job) = jobs.pop() { println!("You need to: {}", job.1); } }
Ia akan selalu mencetak:
You need to: Write back to email from the CEO
You need to: Finish the report today
You need to: Tell your team members thanks for always working hard
You need to: Plan who to hire next for the team
You need to: Watch some YouTube
VecDeque
VecDeque
adalah Vec
yang mana ia bisa melakukan pop entah dari depan ataupun dari belakang. Rust memiliki VecDeque
dikarenakan Vec
sangat baik dalam hal melakukan pop dari belakang (item terakhir), tapi tidak begitu baik untuk melakukan pop dari depan. Saat Anda melihat .pop()
pada Vec
, ia hanya mengambil item terakhir (ujung paling kanan) dan tidak ada posisi dari element vec yang berpindah. Namun jika Anda melakukannya dari arah sebaliknya (melakukan pop di ujung kiri), semua item yang berada di sebelah kanan akan bergeser 1 langkah ke kiri. Anda bisa melihat ini pada deskripsi tentang .remove()
:
Removes and returns the element at position index within the vector, shifting all elements after it to the left.
Sehingga, jika kamu menggunakannya:
fn main() { let mut my_vec = vec![9, 8, 7, 6, 5]; my_vec.remove(0); }
ia akan menghapus 9
. 8
yang berada pada index ke-1 akan berpindah ke index ke-0, 7
yang berada pada index ke-2 akan berpindah ke index ke-1, dan seterusnya. Bayangkan sebuah tempat parkir mobil dimana setiap satu mobil keluar dari tempat parkir tersebut, maka antrian parkiran dibelakangnya akan bergerak maju ke depan.
Sebagai contoh, code dibawah ini banyak menyita kinerja komputer. Faktanya, jika Anda menjalankan ini pada Rust Playground, ada kemungkinan Playground akan menyerah karena cara ini benar-benar memakan banyak resource.
fn main() { let mut my_vec = vec![0; 600_000]; for i in 0..600000 { my_vec.remove(0); } }
Program di atas menggunakan Vec
dengan 600,000 element yang mana elementnya adalah 0. Setiap kali kita menggunakan remove(0)
pada vector tersebut, ia akan menggeser setiap 0 ke kiri sebanyak satu langkah. Dan hal ini secara berulang dilakukan sebanyak 600,000 kali.
Anda tidak perlu mengkhawatirkan hal tersebut jika Anda menggunakan VecDeque
. Ia biasanya memanglah lebih lambat daripada Vec
, namun jika Anda perlu melakukan sesuatu pada kedua ujung VecDeque
tersebut (contohnya pop) maka ia relatif lebih cepat. Anda cukup menggunakan VecDeque::from
pada Vec
untuk membuatnya. Code sebelumnya kita ubah menjadi seperti ini:
use std::collections::VecDeque; fn main() { let mut my_vec = VecDeque::from(vec![0; 600000]); for i in 0..600000 { my_vec.pop_front(); // pop_front sama seperti .pop hanya saja dilakukan dari depan } }
Sekarang ia menjadi lebih cepat, dan pada Playground ia dijalankan pada hitungan detik alih-alih terhenti ditengah jalan.
Pada contoh selanjutnya, kita memiliki Vec
yang berisi list to-do. Kemudian kita buat sebuah VecDeque
dan menggunakan .push_front()
meletakkan mereka dari depan, jadinya item pertama yang kita tambahkan akan berada di sebelah kanan. Tapi setiap item yang kita push typenya adalah (&str, bool)
: &str
adalah deskripsi dan false
berarti tasknya belum dikerjakan. Kita buat dan gunakan function done()
untuk melakukan pop item yang berada di belakang, namun kita tidak ingin menghapusnya. Alih-alih menghapusnya, kita ubah false
menjadi true
dan mem-pushnya ke bagian depan sehingga kita masih tetap menyimpannya.
Codenya seperti berikut:
use std::collections::VecDeque; fn check_remaining(input: &VecDeque<(&str, bool)>) { // Setiap item bertype (&str, bool) for item in input { if item.1 == false { println!("You must: {}", item.0); } } } fn done(input: &mut VecDeque<(&str, bool)>) { let mut task_done = input.pop_back().unwrap(); // pop element yang berada di belakang task_done.1 = true; // sekarang jadikan statusnya sebagai done - ganti menjadi true input.push_front(task_done); // letakkan ia dibagian depan menggunakan .push_front() } fn main() { let mut my_vecdeque = VecDeque::new(); let things_to_do = vec!["send email to customer", "add new product to list", "phone Loki back"]; for thing in things_to_do { my_vecdeque.push_front((thing, false)); } done(&mut my_vecdeque); done(&mut my_vecdeque); check_remaining(&my_vecdeque); for task in my_vecdeque { print!("{:?} ", task); } }
Hasilnya adalah:
You must: phone Loki back
("add new product to list", true) ("send email to customer", true) ("phone Loki back", false)