Rust - Closures Return Functions
Rust'ta Closure Dönen Fonksiyonlar: Fn, FnMut ve FnOnce
Konu: Closure'ları Üretip impl Trait ile Dönmek
Bu kod, bir fonksiyonun içinden closure üretip döndürmeyi ve Rust'ın üç closure trait'ini (Fn, FnMut, FnOnce) karşılaştırmayı gösteriyor.
Parça 1: Fn Dönen Üretici Fonksiyon
fn create_a_fn() -> impl Fn() {
// Yakalanacak metin heap'te bir String olarak oluşturuluyor
let text = "Fn".to_owned();
// move: text closure'a sahiplikle taşınır.
// Sonunda ; yok çünkü closure ifadesi fonksiyonun dönen değeridir.
move || println!("This is a: {}", text)
}
- Ne:
Fn()trait'ini uygulayan bir closure üretir ve döndürür. - Neden
move:textyerel değişkendir;moveolmadan closure referansla yakalar ve fonksiyon sonundatextdüşeceği için derleyici hata verirdi.move, sahipliği closure'a devreder. - Neden
impl Fn(): Closure'ların her biri farklı, isimsiz bir tiptir.impl Traitile "tipi bilinmeyen amaFn()uygulayan bir şey dönüyor" demiş oluruz. - Neden
;yok: Closure, fonksiyonun son ifadesi olduğu için implicit return'dür.returnyazmak da doğru olurdu ama idiomatik değil.
Parça 2: FnMut Dönen Üretici Fonksiyon
fn create_fn_mut() -> impl FnMut() {
let text = "FnMut".to_owned();
// FnMut, yakaladığı durumu değiştirebilen closure'ların trait'idir
move || println!("This is a: {}", text)
}
- Ne:
FnMut()uygulayan bir closure döndürür. - Kim/Nerede: Durumunu (
self'i) değiştiren closure'larFnMutolur; örneğin sayaç tutan bir closure buraya örnek verilebilir. - Neden önemli: Bu closure aslında hiçbir şeyi değiştirmiyor; yani
Fnde uygular. Rust'ta hiyerarşi şöyledir:Fn⊂FnMut⊂FnOnce. HerFn, aynı zamandaFnMutveFnOnce'tır. - Pratik sonuç:
FnMutdöndüğü için çağırıcıdamutbağlaması gerekir (Parça 4'te görünüyor).
Parça 3: FnOnce Dönen Üretici Fonksiyon
fn create_fn_once() -> impl FnOnce() {
let text = "FnOnce".to_owned();
// FnOnce closure'ı çağrıldığında sahipliği tüketir; sadece bir kez çağrılabilir
move || println!("This is a: {}", text)
}
- Ne:
FnOnce()uygulayan bir closure döndürür. - Neden adı "Once": Bu trait'te
selfdeğer olarak alınır; çağrı sırasında closure tüketilir. Bu yüzden birFnOnceclosure yalnızca bir kez çağrılabilir. - Ne zaman gerekir: Closure, yakaladığı değeri başka bir fonksiyona taşıyorsa (örneğin
String'idropetmek veya argüman olarak vermek)FnOnceolmak zorundadır. - Not: Bu örnek yine sadece yazdırıyor, dolayısıyla
FnOnceen geniş (en az kısıtlayıcı üst) imzadır.
Parça 4: Üreticileri Çağıran ve Kullanan Fonksiyon
pub fn execute_closure_output_parameter() {
// Fn: sabit bağlamaya atanabilir, istediğin kadar çağrılabilir
let fn_plain = create_a_fn();
// FnMut: çağrı sırasında self'i değiştirebileceği için mut bağlama zorunlu
let mut fn_mut = create_fn_mut();
// FnOnce: tüketilebilir; tek çağrı sonrası artık kullanılamaz
let fn_once = create_fn_once();
fn_plain(); // "This is a: Fn"
fn_mut(); // "This is a: FnMut"
fn_once(); // "This is a: FnOnce"
}
- Ne: Üç üreticiyi çağırır, üretilen closure'ları değişkenlere bağlar ve çalıştırır.
- Neden
let mut fn_mut:FnMut::call_mut(&mut self, ...)imzası nedeniyle, çağrı değişkeni değiştirebilir; Rust bunu ancakmutbağlamayla izin verir. Derleyiciye sorsaydınız: "cannot borrowfn_mutas mutable, as it is not declared as mutable". - Neden
fn_oncetekrar çağrılamaz: İkincifn_once();satırı derleme hatası verirdi:value used after move.
Çıktı
This is a: Fn
This is a: FnMut
This is a: FnOnce
Özet ve Sonuç
| Parça | Dönen Trait | Bağlama | Kaç kez çağrılabilir |
|---|---|---|---|
create_a_fn |
Fn |
let |
Sınırsız |
create_fn_mut |
FnMut |
let mut |
Sınırsız (mutabıl) |
create_fn_once |
FnOnce |
let |
Yalnızca bir kez |
Parçalar arası ilişki ve veri akışı:
- Her üretici fonksiyon kendi yerel
String'ini oluşturur vemoveile ürettiği closure'ın sahipliğine devreder. Fonksiyon döndüğündetextdüşmez, çünkü artık sahibi closure'dır — bu,move'un varlık sebebidir. impl Fn()/FnMut()/FnOnce()imzaları, isimsiz closure tiplerini soyutlayarak çağıran tarafa tip bilgisi olmadan erişim imkânı sunar.execute_closure_output_parameterüçünü de bağlar ve çağırır;mutzorunluluğu ile tek kullanımlık kısıtı, trait hiyerarşisinin (Fn⊂FnMut⊂FnOnce) doğal sonucudur.
Öğrenilecek ana ders: Kısıtlayıcı trait'i seçmek API sözleşmesini belirler. FnOnce dönmek esnekliği azaltır, Fn dönmek en kullanışlısıdır; closure'ınız mümkünse derleyici bunu otomatik olarak en kısıtlayıcı uygun trait'e atar. Kod okunurluğu açısından, gerçekten durum değiştirmeyen closure'lar için Fn imzası tercih edilmelidir — bu örnek eğitim amaçlı olarak üçünü de ayrı ayrı gösterir.