পাঠ ১৫.৩

Drop trait দিয়ে cleanup-এ code চালানো

Running Code on Cleanup with the Drop Trait

Smart pointer-এর জন্য দ্বিতীয় গুরুত্বপূর্ণ trait হলো Drop — value scope-এর বাইরে যাওয়ার ঠিক আগে কী ঘটবে সেটা customize করতে দেয়। যেকোনো type-এ Drop implement করা যায়, এবং সেই code-এ file বা network connection-এর মতো resource release করা যায়।

Smart pointer-এর context-এ Drop দেখাচ্ছি কারণ smart pointer implement করতে গেলে প্রায় সবসময়ই Drop লাগে। যেমন — Box<T> drop হলে heap-এ যেই memory allocate করেছিল সেটা release করে।

Manual cleanup-এর সমস্যা

কোনো কোনো language-এ programmer-কে নিজে memory free করতে হয় — file handle, socket, lock — সব। ভুলে গেলে system overload হয়ে crash। Rust-এ — value scope-এর বাইরে গেলেই compiler automatic cleanup-code insert করে। তুমি প্রতিটা use-এর শেষে cleanup ভাবার ঝামেলায় পড়ো না, আবার resource leak-ও হয় না।

Cleanup-এর code লিখতে হয় Drop trait implement করে। এই trait-এ একটাই method — drop, যেটা &mut self নেয়। কখন Rust এই drop call করে সেটা দেখার জন্য একটা println!-যুক্ত example।

CustomSmartPointer example

src/main.rsrust
struct CustomSmartPointer {
    data: String,
}

impl Drop for CustomSmartPointer {
    fn drop(&mut self) {
        println!("Dropping CustomSmartPointer with data `{}`!", self.data);
    }
}

fn main() {
    let c = CustomSmartPointer {
        data: String::from("my stuff"),
    };
    let d = CustomSmartPointer {
        data: String::from("other stuff"),
    };
    println!("CustomSmartPointers created");
}

Drop trait prelude-এ আছে — import লাগে না। drop-এর body-তে যা লিখবে সেটাই instance scope-এর বাইরে গেলে চলবে।

main-এ দু'টো instance বানিয়েছি, তারপর "CustomSmartPointers created" print। main শেষ হলে instance দু'টো scope-এর বাইরে যাবে — Rust automatic drop call করবে। আমরা explicit কিছু call করিনি।

Output:

$ cargo run
   Compiling drop-example v0.1.0 (file:///projects/drop-example)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.60s
     Running `target/debug/drop-example`
CustomSmartPointers created
Dropping CustomSmartPointer with data `other stuff`!
Dropping CustomSmartPointer with data `my stuff`!

লক্ষ্য করো — drop-এর order creation-এর উল্টো। d পরে বানানো, তাই আগে drop। এই pattern সবসময় — variable reverse order-এ drop হয়।

Manually drop call করা — নিষেধ

কখনো early cleanup দরকার — যেমন lock manage করা smart pointer। তখন ঘড়ির আগে lock release করা চাই যাতে অন্য code lock নিতে পারে। কিন্তু Rust-এ Drop::drop manually call করতে দেয় না। চেষ্টা করলে:

src/main.rsrust
struct CustomSmartPointer {
    data: String,
}

impl Drop for CustomSmartPointer {
    fn drop(&mut self) {
        println!("Dropping CustomSmartPointer with data `{}`!", self.data);
    }
}

fn main() {
    let c = CustomSmartPointer {
        data: String::from("some data"),
    };
    println!("CustomSmartPointer created");
    c.drop();
    println!("CustomSmartPointer dropped before the end of main");
}
compile errortext
error[E0040]: explicit use of destructor method
  --> src/main.rs:16:7
   |
16 |     c.drop();
   |       ^^^^ explicit destructor calls not allowed
   |
help: consider using `drop` function
   |
16 -     c.drop();
16 +     drop(c);
   |

Error-এ destructor term — programming-এ যে function instance cleanup করে। Constructor-এর উল্টো; Rust-এ drop একটা destructor।

Manual call এজন্য নিষেধ — Rust শেষে আবার automatic drop call করত, double free হত। সেটা memory corruption।

std::mem::drop দিয়ে early cleanup

Early drop চাইলে — standard library-র std::mem::drop function। Prelude-এ আছে, সরাসরি drop(value) লিখলেই হয়।

src/main.rsrust
struct CustomSmartPointer {
    data: String,
}

impl Drop for CustomSmartPointer {
    fn drop(&mut self) {
        println!("Dropping CustomSmartPointer with data `{}`!", self.data);
    }
}

fn main() {
    let c = CustomSmartPointer {
        data: String::from("some data"),
    };
    println!("CustomSmartPointer created");
    drop(c);
    println!("CustomSmartPointer dropped before the end of main");
}
$ cargo run
   Compiling drop-example v0.1.0 (file:///projects/drop-example)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.73s
     Running `target/debug/drop-example`
CustomSmartPointer created
Dropping CustomSmartPointer with data `some data`!
CustomSmartPointer dropped before the end of main

"Dropping..." line "created" আর "dropped before..."-এর মাঝে আসছে — মানে main শেষের আগেই drop হয়ে গেছে।

std::mem::drop ownership নিয়ে নেয়, তারপর সেই function-এর scope শেষে value স্বাভাবিক drop। তাই double free-এর ভয় নেই।

কেন এটা powerful

Drop trait + ownership system মিলে — তোমাকে cleanup মনে রাখতে হয় না, Rust নিজে করে। আবার accidentally use-হওয়া value cleanup হবে না — কারণ ownership rule বলে reference সবসময় valid; drop-ও তখনই হয় যখন value আর use হবে না। নিজের memory allocator লেখা থেকে শুরু করে যেকোনো resource management — Drop দিয়েই।

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

  • Drop trait — value scope-এর শেষে কী ঘটবে customize; smart pointer-এর foundation।
  • drop(&mut self) — automatic call হয়, তুমি explicit কিছু লেখো না।
  • Variable creation-এর reverse order-এ drop হয়।
  • Drop::drop manually call করা যায় না (double free এড়ানোর জন্য)।
  • Early cleanup চাইলে std::mem::drop(value) — ownership নেয়, সেই scope-এর শেষে স্বাভাবিক drop।