Rust - Closures as Parameters in Functions
Closure'ları Parametre Olarak Kabul Eden Fonksiyonlar
Konu: Fn() Trait Bound'u ile Closure ve Fonksiyon'u Birleştirmek
Bu kod, bir fonksiyona closure ile sıradan fn'nin aynı jenerik parametre üzerinden nasıl geçirildiğini gösteriyor.
Parça 1: Closure Üreten ve Çağıran Public Fonksiyon
// Bu fonksiyon "public": dış modüllerden de çağrılabilir.
pub fn execute_closure_input_function() {
// Bir closure tanımlıyoruz: argümansız, değer döndürmüyor.
// Derleyici bunun için ANONİM bir tip üretir ve Fn trait'ini implement eder.
let closure = || println!("I'm a closure!");
// Aynı fonksiyona önce closure, sonra sıradan bir fn geçiriyoruz.
// İkisi de Fn() trait'ini sağladığı için kabul edilir.
call_this(closure); // closure anonim tiptir
call_this(sample_function); // fn pointer ise fn item tipindedir
}
- Ne: Bir closure tanımlar, sonra hem closure'ı hem sıradan bir fonksiyonu
call_this'e argüman olarak geçirir. - Neden closure anonim tiplidir: Her closure için derleyici benzersiz, adı olmayan bir struct benzeri tip üretir. Bu yüzden
let closure: ??? = ...diye elle tip yazamazsınız; tip sadece derleyici tarafından bilinir. - Neden
fnde kabul edilir:sample_functionbir function item tipindedir ve —bir şey yakalamadığı için— otomatik olarakFn()uygular. Bu sayede jenerikcall_thisikisini de kabul eder. - Kim/Nerede:
pubolduğu için crate dışından crate'in public API'si olarak çağrılabilir.
Parça 2: Sıradan Fonksiyon
// Sıradan (closure olmayan) bir fonksiyon.
fn sample_function() {
println!("I'm a function!");
}
- Ne: Closure olmayan, argümansız bir fonksiyondur.
- Neden önemli: Rust'ta closure ile fonksiyon, aynı trait'i (
Fn()) uygulayabildiği için jenerik kodda birbirinin yerine geçebilir. Bu, kodun ana dersidir.
Parça 3: Fn() Bound'lu Jenerik Çağırıcı
// "call_this" herhangi bir ÇAĞRILABİLİR şeyi kabul eder,
// ama şartı var: argümansız çağrılabilen ve bir şey döndürmeyen (Fn()).
// F generic olduğu için closure'ın anonim tipini de kabul edebilir.
fn call_this<F: Fn()>(f: F) {
f(); // gelen şeyi çağırıyoruz.
}
- Ne:
F: Fn()bound'u ile, çağrılabilir ve değer döndürmeyen her türlü tipi kabul eden jenerik bir fonksiyon. - Neden jenerik: Alternatifi
f: fn()(ham fn pointer) olurdu; ancak bu sadece fn item'ları kabul eder, closure'ları reddederdi. Jenerikimpl/bound yaklaşımı closure'ın anonim tipini de monomorfizasyon yoluyla destekler. - Neden
Fn()veFnOnce()değil:FnOncealsaydıkftek kullanımlık olurdu;Fnbirden çok çağrı hakkı verir ve çağıran tarafı kısıtlamaz.
Çıktı
I'm a closure!
I'm a function!
Özet ve Sonuç
Parçalar arası ilişki ve veri akışı:
- Parça 1'de closure, Parça 2'de fn item üretilir; ikisi de Parça 3'ün
F: Fn()parametresine taşınır (ownershipcall_this'e geçer). call_thisgelen değerin tipini bilmez; yalnızcaFn()sözleşmesine güvenir vef()ile çağırır. Bu, "struct'lar yerine davranıştan programlama" (duck typing'in derleme zamanı garantili hali) anlamına gelir.- Önceki makaledeki
execute_closure_output_parameterclosure üretip döndürüyordu; bu örnektekiexecute_closure_input_functionise closure'ı parametre olarak kabul ediyor. İkisi birlikte Rust'ta closure'ların birinci sınıf değerler olduğunu kanıtlar: üretilebilir, döndürülebilir, taşınabilir ve başka fonksiyonlara argüman olarak verilebilir.
Öğrenilecek ana ders: Fonksiyona "çağrılabilir" kabul ettirmenin idiomatik yolu ham fn pointer değil, Fn/FnMut/FnOnce trait bound'lu jenerik parametredir. Bu sayede hem closure hem fonksiyon aynı API'yi paylaşır.