পাঠ ২০.৪

Advanced Function এবং Closure

Advanced Functions and Closures

Function-কে argument-এ পাস করার সময় closure-এর পাশাপাশি regular function-ও দেওয়া যায় — সেটার type fn (lower case f), যাকে বলে function pointer। আবার function থেকে closure return করতে কিছু বিশেষ কৌশল লাগে। দু'টোই দেখব।

Function pointer

fn type — closure trait Fn-এর সাথে গুলিয়ো না। Function-গুলো fn type-এ coerce হয়:

fn add_one(x: i32) -> i32 {
    x + 1
}

fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
    f(arg) + f(arg)
}

fn main() {
    let answer = do_twice(add_one, 5);

    println!("The answer is: {answer}");
}

Output: The answer is: 12। এখানে f: fn(i32) -> i32 — মানে এমন একটা function pointer যা i32 নেয় ও i32 return করে। Closure-এর মতো generic-এ trait bound লাগে না; fn নিজেই concrete type।

Function pointer তিনটা closure trait-এর প্রতিটাই (Fn,FnMut, FnOnce) implement করে। তাই closure-আশা-করা function-এ function pointer পাস করা যায়। Best practice — generic বানিয়ে closure trait bound দাও, তাহলে user দু'টোই দিতে পারবে।

যেখানে শুধু fn চাই (closure না) — C-র মতো language-এর সাথে interop। C-তে closure নেই।

Iterator method-এর সাথে

Closure-এ:

fn main() {
    let list_of_numbers = vec![1, 2, 3];
    let list_of_strings: Vec<String> =
        list_of_numbers.iter().map(|i| i.to_string()).collect();
}

সরাসরি function name দিলেও কাজ করে:

fn main() {
    let list_of_numbers = vec![1, 2, 3];
    let list_of_strings: Vec<String> =
        list_of_numbers.iter().map(ToString::to_string).collect();
}

Fully qualified syntax লাগছে কারণ একাধিক to_string থাকতে পারে। এখানে ToString trait-এর-টা — যেটা যেকোনো Display-এ blanket implementation দিয়ে available।

Enum initializer-ও function pointer

Tuple-variant-এর enum-এ variant name আসলে initializer function। এটাও closure-যেখানে চাই, ব্যবহার করা যায়:

fn main() {
    enum Status {
        Value(u32),
        Stop,
    }

    let list_of_statuses: Vec<Status> = (0u32..20).map(Status::Value).collect();
}

0 থেকে 19 প্রতিটা u32-কে Status::Value variant-এ wrap। দু'টো style-ই (closure বা initializer function) একই code-এ compile হয় — যেটা পরিষ্কার লাগে সেটাই ব্যবহার করো।

Closure return করা

Closure trait দিয়ে represent — সরাসরি return করা যায় না। Function-এর মতো concrete type ও নেই (একটা closure নিজেই unique type)। সমাধান — impl Trait:

fn returns_closure() -> impl Fn(i32) -> i32 {
    |x| x + 1
}

কিন্তু একই signature-এর একাধিক closure return করতে গেলে — সমস্যা। কারণ প্রতিটা closure আলাদা concrete type:

fn main() {
    let handlers = vec![returns_closure(), returns_initialized_closure(123)];
    for handler in handlers {
        let output = handler(5);
        println!("{output}");
    }
}

fn returns_closure() -> impl Fn(i32) -> i32 {
    |x| x + 1
}

fn returns_initialized_closure(init: i32) -> impl Fn(i32) -> i32 {
    move |x| x + init
}
compile errortext
error[E0308]: mismatched types
  --> src/main.rs:2:44
   |
 2 |     let handlers = vec![returns_closure(), returns_initialized_closure(123)];
   |                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected opaque type, found a different opaque type
   |
   = note: distinct uses of `impl Trait` result in different opaque types

impl Fn প্রতিবার একটা opaque type তৈরি করে — দু'জায়গার opaque type same না।

Trait object-এ সমাধান

Box<dyn Fn(...) -> ...> ব্যবহার করো — সব closure একই trait object type-এ:

fn main() {
    let handlers = vec![returns_closure(), returns_initialized_closure(123)];
    for handler in handlers {
        let output = handler(5);
        println!("{output}");
    }
}

fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
    Box::new(|x| x + 1)
}

fn returns_initialized_closure(init: i32) -> Box<dyn Fn(i32) -> i32> {
    Box::new(move |x| x + init)
}

এখন vector-এ একই type-এর closure storage সম্ভব। দাম — heap allocation ও dynamic dispatch।

এই পাঠ থেকে যা শিখলে

  • fn type — function pointer; closure trait Fn-এর সাথে গুলিও না।
  • Function pointer তিনটা closure trait-ই implement করে — তাই closure-আশা-করা জায়গায় ব্যবহারযোগ্য।
  • Enum tuple-variant-এর initializer function — function pointer হিসেবে যেমন Status::Value
  • Closure return — সিম্পল ক্ষেত্রে impl Fn(...) -> ...
  • একাধিক closure এক collection-এ — Box<dyn Fn(...) -> ...> trait object।
  • FFI-জাতীয় কাজে — যেখানে অন্য language-এ closure নেই — শুধু fn type accept করো।