Pada chapter sebelumnya kita melihat bahwa for name in menu.iter() berhasil mengiterasi koleksi tanpa mengganggunya, dan next yang dipanggil manual menghasilkan nilai bertipe reference. Sebenarnya Rust menyediakan tiga method untuk memulai iterasi — iter, iter_mut, dan into_iter — dan ketiganya memetakan persis ke tiga cara mengakses data yang sudah kita kuasai di section Ownership: membaca lewat pinjaman &T, mengubah lewat pinjaman &mut T, atau memiliki nilai itu sendiri T. Memilih bentuk yang tepat berarti memilih konsekuensi kepemilikan yang tepat pula, karena salah satu bentuk akan memindahkan koleksi kita secara permanen. Pada chapter ini kita bedah ketiganya satu per satu, lengkap dengan bentuk singkat for yang menjadi gula sintaksisnya.
Tiga Wajah Iterasi pada Vec #
Sebelum masuk ke kode, mari kita petakan pilihan yang tersedia untuk sebuah koleksi, misalnya Vec<String> bernama menu:
| Bentuk | Method | Elemen yang dihasilkan | Konsekuensi kepemilikan |
|---|---|---|---|
| Pinjam baca | menu.iter() | &String | menu tetap milik kita dan tidak berubah |
| Pinjam ubah | menu.iter_mut() | &mut String | menu tetap milik kita, isinya boleh diubah di tempat |
| Pindahkan | menu.into_iter() | String | kepemilikan elemen berpindah ke perulangan; koleksi kosong |
Tabel di atas adalah cerminan langsung aturan borrowing: satu jalur baca bersama, satu jalur tulis eksklusif, atau penyerahan kepemilikan penuh. Tidak ada bentuk keempat, dan kompilator akan memastikan kita tidak mendapatkan lebih dari yang dibayarkan.
iter: Meminjam untuk Membaca #
Bentuk paling sering dipakai sehari-hari adalah iter(). Iterator hasilnya menyerahkan reference ke setiap elemen, sehingga koleksi aslinya sama sekali tidak tersentuh:
fn main() {
let menu = vec![String::from("Kopi Gayo"), String::from("Teh Melati")];
for name in menu.iter() {
println!("Menu: {}", name);
}
println!("Koleksi masih utuh: {} item", menu.len());
}Output yang Dihasilkan:
Menu: Kopi Gayo
Menu: Teh Melati
Koleksi masih utuh: 2 itemMari kita bedah baris demi baris:
- Baris 2 (
let menu = vec![...]):menumenjadi pemilikVec<String>berisi dua nama minuman. - Baris 4 (
for name in menu.iter()):menu.iter()meminjam koleksi secara immutable dan membuat iterator yang menyerahkan&Stringper putaran. Variabelnamebertipe&String, bukanString— sesuai dengan yang kita lihat pada outputSome(15000)bertipe&i32di chapter sebelumnya. - Baris 5 (
println!("Menu: {}", name);): pemformatan membaca isiStringmelalui reference tanpa memindahkan atau mengklonnya. - Baris 8 (
menu.len()): setelah perulangan selesai,menumasih hidup dan dapat dipakai penuh — bukti bahwaiter()hanya meminjam. Pinjaman baca juga boleh dipegang bersamaan dengan pengaksesan lain, sehingga kita bisa membuat dua iteratoriter()dari koleksi yang sama tanpa konflik.
into_iter: Memindahkan Kepemilikan #
Bentuk kedua, into_iter(), memulai iterasi dengan menyerahkan kepemilikan koleksi itu sendiri. Kata into pada namanya adalah petunjuk: data masuk ke dalam iterator dan tidak kembali. Setiap elemen kini menjadi milik badan perulangan:
fn main() {
let menu = vec![String::from("Kopi Gayo"), String::from("Teh Melati")];
for name in menu.into_iter() {
println!("Nama menu: {}", name);
}
}Output yang Dihasilkan:
Nama menu: Kopi Gayo
Nama menu: Teh MelatiProgram ini berjalan mulus, tetapi ada harga yang harus dibayar. Coba kita tambahkan satu baris setelah perulangan yang mengakses menu seperti pada contoh iter() sebelumnya:
fn main() {
let menu = vec![String::from("Kopi Gayo"), String::from("Teh Melati")];
for name in menu.into_iter() {
println!("Menu: {}", name);
}
println!("Koleksi masih utuh: {} item", menu.len());
}Kompilator menolak program tersebut dengan pesan galat asli berikut:
error[E0382]: borrow of moved value: `menu`
--> main.rs:8:45
|
2 | let menu = vec![String::from("Kopi Gayo"), String::from("Teh Melati")];
| ---- move occurs because `menu` has type `Vec<String>`, which does not implement the `Copy` trait
3 |
4 | for name in menu.into_iter() {
| ----------- `menu` moved due to this method call
...
8 | println!("Koleksi masih utuh: {} item", menu.len());
| ^^^^ value borrowed here after move
|
note: `std::iter::IntoIterator::into_iter` takes ownership of the receiver `self`, which moves `menu`
--> /rustc/b940084d7eb6a299eb4bfeb8e34901bc051e7ac4/library/core/src/iter/traits/collect.rs:312:17
help: you can `clone` the value and consume it, but this might not be your desired behavior
|
4 | for name in menu.clone().into_iter() {
| ++++++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0382`.Pesan E0382 ini persis dengan pesan kepemilikan yang kita temui di section Ownership: menu berpindah karena pemanggilan into_iter() pada baris 4, lalu menu.len() pada baris 8 mencoba meminjam nilai yang sudah tidak dimiliki siapa pun di scope ini. Perhatikan pula catatan kompilator: std::iter::IntoIterator::into_iter secara eksplisit mengambil kepemilikan receiver self. Inilah bukti tertulis bahwa memilih into_iter() sama dengan menyerahkan koleksi secara penuh.
iter_mut: Meminjam untuk Mengubah #
Bentuk ketiga menyelesaikan kebutuhan yang tidak bisa dipenuhi dua bentuk lainnya: mengubah setiap elemen di tempat. Toko kopi kita sedang mengadakan promosi potongan Rp1.000 untuk seluruh menu:
fn main() {
let mut prices = vec![15000, 17500, 20000];
for price in prices.iter_mut() {
*price = *price - 1000;
}
println!("Harga setelah promosi: {:?}", prices);
}Output yang Dihasilkan:
Harga setelah promosi: [14000, 16500, 19000]Uraian baris-per-barisnya:
- Baris 2 (
let mut prices = vec![15000, 17500, 20000];): koleksi harus dideklarasikanmutkarena isinya akan diubah. Tanpamut, kompilator menolak pembuatan iteratoriter_mut()sejak awal. - Baris 4 (
for price in prices.iter_mut()): iterator meminjam koleksi secara mutable, satu elemen pada satu waktu, dan menyerahkan&mut i32per putaran. Sesuai aturan dari section Ownership, hanya satu pinjaman mutable yang aktif dalam satu momen — iteratoriter_mut()adalah satu-satunya pemegang pinjaman itu selama perulangan berlangsung. - Baris 5 (
*price = *price - 1000;): operator*melakukan dereference — membaca nilai yang ditunjuk reference (*pricedi sisi kanan), menguranginya 1000, lalu menulis hasilnya kembali ke lokasi yang sama (*pricedi sisi kiri). Koleksi diubah tanpa penyalinan dan tanpa pemindahan. - Baris 8 (
println!(..., prices)): pinjaman mutable berakhir bersama perulangan, jadipricesdapat dibaca kembali — dan output membuktikan ketiga harga benar berubah.
Bentuk Singkat for dan Desugaring #
Ketiga method tersebut punya padanan sintaksis yang lebih ringkas, dan Rust mengatur pemetaannya lewat trait IntoIterator:
for x in &collectionsetarafor x in collection.iter()— pinjam baca.for x in &mut collectionsetarafor x in collection.iter_mut()— pinjam ubah.for x in collectionsetarafor x in collection.into_iter()— pindahkan.
Bentuk singkat ini bukan sihir. Kompilator menerjemahkan for menjadi urutan panggilan next — penulisan manual berikut menghasilkan perilaku yang identik dengan for name in menu.iter():
fn main() {
let menu = vec!["Kopi Gayo", "Teh Melati"];
let mut iterator = menu.iter();
while let Some(name) = iterator.next() {
println!("Menu: {}", name);
}
}Output yang Dihasilkan:
Menu: Kopi Gayo
Menu: Teh MelatiMari kita bedah terjemahan manualnya:
- Baris 4 (
let mut iterator = menu.iter();): iterator dibuat dan disimpan di variabelmut— langkah yang biasanya disembunyikan olehfor. - Baris 6 (
while let Some(name) = iterator.next()): setiap putaran memanggilnext, lalu polaSome(name)membedahOption. Selama hasilnyaSome, badan perulangan berjalan dannameterikat pada nilai di dalamnya. KetikanextmengembalikanNone, pola gagal dicocokkan danwhileberhenti — persis perilaku driver yang kita uraikan di chapter sebelumnya. - Baris 7 (
println!("Menu: {}", name);): badan perulangan bekerja normal, tanpa menyadari bahwa bentuk aslinya adalah perulanganfor.
Ringkasan #
Tiga bentuk iterasi Vec memetakan langsung ke tiga model akses data: iter() menyerahkan &T dan membiarkan koleksi utuh, iter_mut() menyerahkan &mut T untuk mengubah elemen di tempat, dan into_iter() memindahkan kepemilikan elemen ke perulangan hingga koleksi asli tidak dapat dipakai lagi — pelanggarannya ditangkap sebagai E0382. Bentuk singkat for x in &v, for x in &mut v, dan for x in v adalah gula sintaksis ketiganya, yang di balik layar diterjemahkan menjadi pemanggilan next berulang sampai None. Satu sifat penting ketiga bentuk ini belum kita bahas: iterator sebenarnya tidak melakukan pekerjaan apa pun sampai benar-benar dipaksa. Sifat malas inilah yang menjadi pokok chapter berikutnya.