Rust'ta standart koleksiyon kütüphanesi tamamen Iterator konsepti üzerine kurulmuştur. Iterator, next() metodunu tanımlayan Iterator trait'ini uygulayan bir nesnedir. Bu metod, veri dizisinin sonraki elemanını döner (Option<T>, burada Some(T) sonraki değer, None ise dizinin sonudur).
Standart Iterator örneği:
let v = vec![1, 2, 3]; let mut iter = v.iter(); while let Some(x) = iter.next() { println!("{}", x); }
Iterator adaptörleri - yeni iterator'lar döndüren metodlardır (örneğin, .map(), .filter(), .enumerate(), .take()) ve geçilen fonksiyonlara göre değerleri 'anlık' olarak işler.
Özel iteratorlar, bir yapı oluşturularak ve bu yapı için Iterator trait'ini kendi davranışıyla uygulayarak oluşturulur:
struct Counter { count: u8 } impl Iterator for Counter { type Item = u8; fn next(&mut self) -> Option<Self::Item> { if self.count < 5 { self.count += 1; Some(self.count) } else { None } } }
Standart adaptörler, koleksiyonları işlemek için yeterli olduğunda kullanılır. Özel bir iterator uygulamanız gerektiğinde:
Sonsuz iteratorlar oluşturmak mümkün mü?
.collect()gibi bir yöntemle bunları bir koleksiyona toplamaya çalıştığınızda ne olur?
Cevap: Evet, Rust'ta std::iter::repeat gibi sonsuz bir dizi döndüren iterator'lar vardır:
let mut endless = std::iter::repeat(1); endless.next(); // sürekli olarak Some(1) döndürecektir
Böyle bir iterator'ı .collect() ile bir koleksiyona toplamaya çalışırsanız, program takılır ya da bellek taşması nedeniyle çökebilir çünkü iterasyon kendi kendine sona ermeyecektir!
Hikaye
REST API projesinde 1000 elemanlı bir dizi sıralanmış, ardından .iter().filter(|x| *x > 500) kullanılmış, ancak .collect::<Vec<_>>() yerine karmaşık bir adaptör içinde .fold(0, |acc, _| acc + 1) uygulanmıştır, bu sayede iterasyonun geçerli bir şekilde tamamlanıp tamamlanmayacağı konusunda bir anlayış kaybolmuştur. Sonuç: içsel bir hatayla birlikte sınırsız tembel filtrasyon nedeniyle rastgele takılmalar.
Hikaye
Bir özel id üretme motorunda, rastgele bir geliştirici değer limiti aşıldığında None döndürmeyi unutarak kendi iterator'ını uygulamaya karar verdi. Sonuç olarak, iterasyon sonsuz bir döngüye girdi ve sunucu üretim ortamında tüm CPU'yu tüketerek istekleri yanıtlayamadı.
Hikaye
Frontend bölümünde (WebAssembly modülü) iç içe adaptörler kullanan bir iterator: .map().filter().skip(), ve form için sonuç elde etmeye çalışırken .collect() ile işlem yaptılar. Rust, adaptör altındaki tipin değiştirilmesi durumunda, kesin koleksiyon tipini belirtmeyi unuttukları için karmaşık bir derleme zamanı hatası verdi: .collect::<Vec<_>>(). Problemi çözmek için bir anotasyon eklemek gerekti, ancak sebebin peşinde birkaç saat harcadılar.