Iterators
Versi Bahasa Indonesia. Baca dalam Bahasa Inggris
Pola iterator memungkinkan Anda melakukan suatu tugas pada urutan item secara bergantian. Iterator menyediakan cara untuk melintasi, mentransformasi, atau mengonsumsi elemen secara efisien tanpa perlu mengelola index atau loop secara manual.
Iterator Rust bersifat lazy secara default, artinya mereka tidak melakukan operasi sampai secara eksplisit digunakan (consumed).
Closures dan iterators adalah fitur Rust yang terinspirasi dari ide-ide bahasa pemrograman fungsional. Mereka berkontribusi pada kemampuan Rust untuk mengekspresikan ide tingkat tinggi dengan performa tingkat rendah. Implementasi closures dan iterators sedemikian rupa sehingga performa runtime tidak terpengaruh. Ini adalah bagian dari tujuan Rust untuk menyediakan zero-cost abstractions. - Comparing Performance: Loops vs. Iterators
Iterator Trait
Iterator adalah tipe apa pun yang mengimplementasikan Iterator trait. Iterator trait memiliki satu method yang diperlukan:
fn next(&mut self) -> Option<Self::Item>;next mengembalikan item berikutnya dalam urutan yang dibungkus dalam Some atau None jika iterator sudah habis.
Memajukan iterator dan mengembalikan nilai berikutnya.
Mengembalikan
Noneketika iterasi selesai. Implementasi iterator individual dapat memilih untuk melanjutkan iterasi, dan memanggilnext()lagi mungkin atau mungkin tidak mulai mengembalikanSome(Item)lagi di beberapa titik.
Contoh:
fn main() {
let numbers = vec![1, 2, 3];
let mut iter = numbers.iter();
println!("{:?}", iter.next());
println!("{:?}", iter.next());
println!("{:?}", iter.next());
println!("{:?}", iter.next());
}Output:
Some(1)
Some(2)
Some(3)
NoneConsumer Adapters
Iterator trait memiliki sejumlah method berbeda dengan implementasi default yang disediakan oleh standard library. Method yang memanggil next disebut consuming adapters, karena memanggilnya akan menghabiskan iterator.
Beberapa method consumer adapters yang umum digunakan: count, sum, fold, product, collect, dll. Anda dapat menemukan lebih banyak method yang disediakan di sini: https://doc.rust-lang.org/std/iter/trait.Iterator.html#provided-methods.
Contoh
Sum
fn sum<S>(self) -> S
where
Self: Sized,
S: Sum<Self::Item>,-
Menjumlahkan elemen-elemen dari sebuah iterator.
-
Mengambil setiap elemen, menjumlahkannya, dan mengembalikan hasilnya.
-
Iterator kosong mengembalikan nilai nol dari tipe tersebut.
-
Contoh:
fn main() { let numbers = vec![1, 2, 3]; let iter = numbers.iter(); let x: i32 = iter.sum(); println!("{:?}", x); // 6 }
Fold
fn fold<B, F>(self, init: B, f: F) -> B
where
Self: Sized,
F: FnMut(B, Self::Item) -> B,-
Melipat setiap elemen ke dalam akumulator dengan menerapkan operasi, mengembalikan hasil akhir.
-
fold()mengambil dua argumen: nilai awal, dan closure dengan dua argumen: sebuah 'accumulator', dan sebuah elemen. Closure mengembalikan nilai yang harus dimiliki akumulator untuk iterasi berikutnya. -
Nilai awal adalah nilai yang akan dimiliki akumulator pada panggilan pertama.
-
Setelah menerapkan closure ini ke setiap elemen iterator,
fold()mengembalikan akumulator. -
Contoh:
fn main() { let numbers = vec![1, 2, 3]; let iter = numbers.iter(); let x: i32 = iter.fold(0, |acc, &x| acc + x * x); println!("{:?}", x); // 14 }
Iterator Adapters
Iterator adapters adalah method yang didefinisikan pada Iterator trait yang tidak mengonsumsi iterator. Sebaliknya, mereka menghasilkan iterator yang berbeda dengan mengubah beberapa aspek dari iterator asli.
Beberapa method iterator adapters yang umum digunakan: map, filter, take, skip, enumerate, dll. Sekali lagi, Anda dapat menemukan lebih banyak method yang disediakan di sini: https://doc.rust-lang.org/std/iter/trait.Iterator.html#provided-methods.
Contoh
Map
fn map<B, F>(self, f: F) -> Map<Self, F>
where
Self: Sized,
F: FnMut(Self::Item) -> B,-
Mengambil sebuah closure dan membuat iterator yang memanggil closure tersebut pada setiap elemen.
-
map()mengubah satu iterator menjadi iterator lain, melalui argumennya: sesuatu yang mengimplementasikanFnMut. Ia menghasilkan iterator baru yang memanggil closure ini pada setiap elemen dari iterator asli. -
Jika Anda terbiasa berpikir dalam tipe, Anda dapat memikirkan
map()seperti ini: Jika Anda memiliki iterator yang memberi Anda elemen dari beberapa tipeA, dan Anda menginginkan iterator dari beberapa tipe lainB, Anda dapat menggunakanmap(), dengan memberikan closure yang mengambilAdan mengembalikanB.fn main() { let numbers = vec![1, 2, 3]; let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect(); println!("{:?}", doubled); // [2, 4, 6] }
Filter
fn filter<P>(self, predicate: P) -> Filter<Self, P>
where
Self: Sized,
P: FnMut(&Self::Item) -> bool,-
Membuat iterator yang menggunakan closure untuk menentukan apakah suatu elemen harus dihasilkan.
-
Untuk suatu elemen, closure harus mengembalikan
trueataufalse. Iterator yang dikembalikan hanya akan menghasilkan elemen-elemen yang closure-nya mengembalikantrue. -
Contoh:
fn main() { let numbers = vec![1, 2, 3]; // Filter angka yang != 2 let mut iter = numbers.iter().filter(|x| **x != 2); println!("{:?}", iter.next()); // Some(1) println!("{:?}", iter.next()); // Some(3) println!("{:?}", iter.next()); // None }
Kekuatan collect()
fn collect<B>(self) -> B
where
B: FromIterator<Self::Item>,
Self: Sized,Method collect adalah bagian dari Iterator trait dan digunakan untuk mengubah iterator menjadi sebuah collection. Ia bergantung pada FromIterator trait untuk menentukan bagaimana membangun tipe target.
Contoh Vec::from_iter:
fn main() {
let five_fives = std::iter::repeat(5).take(5);
let v = Vec::from_iter(five_fives);
println!("{:?}", v); // [5, 5, 5, 5, 5]
}collect() dapat mengambil apa pun yang iterable, dan mengubahnya menjadi collection yang relevan. Ini adalah salah satu method yang paling kuat di standard library, digunakan dalam berbagai konteks.
Pola paling dasar di mana collect() digunakan adalah mengubah satu collection menjadi collection lain. Anda mengambil sebuah collection, memanggil iter padanya, melakukan serangkaian transformasi, dan kemudian collect() di akhir.
Contoh collect:
fn main() {
fn take_vec(v: Vec<i32>) {
println!("{:?}", v) // [2, 4, 6]
}
let numbers = vec![1, 2, 3];
let doubled = numbers.iter().map(|x| x * 2).collect();
take_vec(doubled); // Compiler menyimpulkan tipe target sebagai Vec<i32>
}- Jadi di sini Rust akan memeriksa tipe target saat
collectdipanggil. - Seperti yang kita lihat, fungsi
take_vecmengharapkan tipeVec<i32>. - Compiler Rust kemudian akan menyimpulkan bahwa target
collectadalahVec<i32>. - Dan karena
VecmemilikiFromIteratortrait, ia akan memanggil fungsiVec::from_iterdan menghasilkan hasilnya.
Kenapa collect Kadang Gagal?
Ada berbagai alasan mengapa terkadang collect tidak dapat membangun tipe yang ditargetkan, di antaranya:
-
Tipe Target Ambigu: Jika tipe target tidak jelas dari konteks.
-
Ketidakcocokan Tipe: Jika iterator menghasilkan item yang tidak dapat dikonversi ke tipe target.
-
Tipe Target Tidak Didukung: Jika tipe target tidak mengimplementasikan
FromIterator.Meskipun demikian, kita dapat mengimplementasikan
FromIteratorjika kita memiliki tipe kustom sendiri seperti ini:fn main() { use std::iter::FromIterator; #[derive(Debug)] struct MyType(Vec<i32>); impl FromIterator<i32> for MyType { fn from_iter<I: IntoIterator<Item = i32>>(iter: I) -> Self { MyType(iter.into_iter().collect()) } } let numbers = vec![1, 2, 3]; let result: MyType = numbers.into_iter().collect(); println!("{:?}", result); // MyType([1, 2, 3]) }
iter vs iter_mut vs into_iter
.iter()
- Mengembalikan iterator dari references (
&T) ke elemen-elemen collection. - Collection tidak dikonsumsi.
- Tidak dapat memodifikasi elemen collection asli (kecuali digabungkan dengan
.iter_mut()).
Contoh:
fn main() {
let numbers = vec![1, 2, 3];
let sum: i32 = numbers.iter().sum();
println!("Sum: {:?}", sum); // Sum: 6
println!("Vec: {:?}", numbers); // Vec: [1, 2, 3]
}.iter_mut()
- Mengembalikan iterator dari mutable references (
&mut T) ke elemen-elemen collection. - Untuk memodifikasi elemen collection di tempat.
Contoh:
fn main() {
let mut numbers = vec![1, 2, 3];
println!("Vec: {:?}", numbers); // Vec: [1, 2, 3]
numbers.iter_mut().for_each(|x| *x *= 2);
println!("Vec: {:?}", numbers); // Vec: [2, 4, 6]
}.into_iter()
- Mengonsumsi collection dan mengembalikan iterator dari owned values (
T). - Memindahkan kepemilikan setiap elemen.
- Setelah memanggil
.into_iter(), collection asli tidak dapat digunakan lagi karena kepemilikan telah ditransfer ke iterator.
Contoh:
fn main() {
let numbers = vec![1, 2, 3];
let doubled: Vec<i32> = numbers.into_iter().map(|x| x * 2).collect();
println!("Vec: {:?}", doubled); // Vec: [2, 4, 6]
println!("Vec: {:?}", numbers); // error[E0382]: borrow of moved value: `numbers`
}Referensi
- https://doc.rust-lang.org/book/ch13-02-iterators.html
- https://doc.rust-lang.org/book/ch13-04-performance.html
- https://doc.rust-lang.org/std/iter/trait.Iterator.html
- https://doc.rust-lang.org/std/iter/trait.FromIterator.html
Last updated on