๐Ÿฆ€ Functional Rust
๐ŸŽฌ Rust Ownership in 30 seconds Visual walkthrough of ownership, moves, and automatic memory management.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Each value in Rust has exactly one owner โ€” when the owner goes out of scope, the value is dropped

โ€ข Assignment moves ownership by default; the original binding becomes invalid

โ€ข Borrowing (&T / &mut T) lets you reference data without taking ownership

โ€ข The compiler enforces: many shared references OR one mutable reference, never both

โ€ข No garbage collector needed โ€” memory is freed deterministically at scope exit

409: Drop Trait and RAII

Difficulty: 2 Level: Intermediate Run custom cleanup code when a value goes out of scope โ€” guaranteed by the borrow checker to run exactly once.

The Problem This Solves

Resources โ€” file handles, network sockets, database connections, locks, memory allocations โ€” must be released when you're done with them. In languages without RAII, you must remember to call `close()`, `free()`, `unlock()` everywhere, including on every error path. Miss one, and you have a resource leak or a deadlock. Rust's `Drop` trait solves this at the language level. When a value goes out of scope โ€” normally, via `return`, via `panic`, or via explicit `std::mem::drop()` โ€” the compiler calls `drop()` automatically. There's no way to forget: the borrow checker tracks ownership and guarantees cleanup runs exactly once. This is RAII (Resource Acquisition Is Initialization): acquire the resource in the constructor, release it in `Drop`. The standard library is built on this. `MutexGuard` unlocks the mutex when dropped. `File` closes the file descriptor when dropped. `Box<T>` frees the heap allocation when dropped. You get the same power for your own resource types.

The Intuition

`Drop` is a single-method trait: `fn drop(&mut self)`. The compiler calls it automatically when the value's lifetime ends. You cannot call `drop()` directly (the compiler prevents double-drop). For early cleanup, use `std::mem::drop(value)` which moves the value into a function that immediately drops it. Drop order is deterministic: in a scope, values are dropped in reverse declaration order (last declared, first dropped โ€” like a stack). Fields in a struct are dropped in declaration order. This is guaranteed and stable.

How It Works in Rust

struct FileHandle {
 name: String,
 is_open: bool,
}

impl FileHandle {
 fn open(name: &str) -> Self {
     println!("Opening: {}", name);
     FileHandle { name: name.to_string(), is_open: true }
 }
}

impl Drop for FileHandle {
 fn drop(&mut self) {
     if self.is_open {
         println!("Closing: {}", self.name); // automatic cleanup
         self.is_open = false;
     }
 }
}

// RAII lock guard: lock acquired on creation, released on drop
struct LockGuard<'a> {
 resource: &'a str,
}

impl<'a> LockGuard<'a> {
 fn acquire(resource: &'a str) -> Self {
     println!("Lock acquired: {}", resource);
     LockGuard { resource }
 }
}

impl<'a> Drop for LockGuard<'a> {
 fn drop(&mut self) {
     println!("Lock released: {}", self.resource);
 }
}

fn main() {
 // Scope-based cleanup
 {
     let f = FileHandle::open("data.txt");
     println!("Using file...");
 }  // drop() called here โ€” "Closing: data.txt"

 // Drop order: reversed declaration order
 {
     let _a = FileHandle::open("a.txt");
     let _b = FileHandle::open("b.txt");
     let _c = FileHandle::open("c.txt");
 }  // Drops: c, b, a (reverse order)

 // Lock guard: always released, even on panic
 {
     let _guard = LockGuard::acquire("database");
     println!("Doing database work...");
     // panicking here would still release the lock
 }  // "Lock released: database"

 // Explicit early drop
 let f2 = FileHandle::open("early.txt");
 println!("Using...");
 std::mem::drop(f2);  // explicit drop before end of scope
 println!("After explicit drop");
}
A `Timer` using RAII for profiling:
use std::time::Instant;
struct Timer { name: String, start: Instant }
impl Timer {
 fn new(name: &str) -> Self { Timer { name: name.to_string(), start: Instant::now() } }
}
impl Drop for Timer {
 fn drop(&mut self) {
     println!("'{}' took {:?}", self.name, self.start.elapsed());
 }
}

fn expensive_function() {
 let _t = Timer::new("expensive_function"); // starts here
 // ... work ...
} // logs elapsed time here, even if function panics

What This Unlocks

Key Differences

ConceptOCamlRust
Cleanup`with_file name f` โ€” explicitly pass a callback; cleanup in `finally``Drop` trait โ€” cleanup auto-runs when value leaves scope
Error safety`Fun.protect ~finally:cleanup f` โ€” handles exceptions`Drop` runs on panic too โ€” no special handling needed
Explicit early cleanup`close_file fh` โ€” manual call`std::mem::drop(value)` โ€” ownership moved and dropped
OrderingFunction scope / `try...finally`Deterministic reverse-declaration order โ€” specified by the language
// Drop and RAII patterns in Rust

struct FileHandle {
    name: String,
    is_open: bool,
}

impl FileHandle {
    fn open(name: &str) -> Self {
        println!("Opening: {}", name);
        FileHandle { name: name.to_string(), is_open: true }
    }

    fn read(&self) -> Option<String> {
        if self.is_open {
            Some(format!("Contents of {}", self.name))
        } else {
            None
        }
    }
}

impl Drop for FileHandle {
    fn drop(&mut self) {
        if self.is_open {
            println!("Closing: {}", self.name);
            self.is_open = false;
        }
    }
}

// RAII guard pattern
struct LockGuard<'a> {
    resource: &'a str,
    lock_id: u32,
}

impl<'a> LockGuard<'a> {
    fn acquire(resource: &'a str) -> Self {
        let id = 42; // simulated lock ID
        println!("Acquired lock #{} on '{}'", id, resource);
        LockGuard { resource, lock_id: id }
    }
}

impl<'a> Drop for LockGuard<'a> {
    fn drop(&mut self) {
        println!("Released lock #{} on '{}'", self.lock_id, self.resource);
    }
}

// Timer for profiling
use std::time::Instant;
struct Timer { name: String, start: Instant }

impl Timer {
    fn new(name: &str) -> Self {
        println!("Starting timer: {}", name);
        Timer { name: name.to_string(), start: Instant::now() }
    }
}

impl Drop for Timer {
    fn drop(&mut self) {
        println!("Timer '{}' elapsed: {:?}", self.name, self.start.elapsed());
    }
}

fn main() {
    println!("=== FileHandle RAII ===");
    {
        let f = FileHandle::open("data.txt");
        println!("{:?}", f.read());
    } // Drop called here โ€” file closed

    println!("\n=== Drop order ===");
    {
        let _a = FileHandle::open("a.txt");
        let _b = FileHandle::open("b.txt");
        let _c = FileHandle::open("c.txt");
    } // Drops in reverse: c, b, a

    println!("\n=== Lock guard ===");
    {
        let _guard = LockGuard::acquire("database");
        println!("Working with database...");
    } // guard dropped, lock released

    println!("\n=== Explicit early drop ===");
    let f2 = FileHandle::open("early.txt");
    println!("{:?}", f2.read());
    std::mem::drop(f2); // explicitly drop before end of scope
    println!("After explicit drop");

    println!("\n=== Timer RAII ===");
    {
        let _t = Timer::new("computation");
        let _sum: u64 = (1..=1000).sum();
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_file_handle() {
        let f = FileHandle::open("test.txt");
        assert!(f.is_open);
        assert!(f.read().is_some());
    }

    #[test]
    fn test_explicit_drop() {
        let f = FileHandle::open("drop_test.txt");
        let name = f.name.clone();
        std::mem::drop(f);
        // After drop, we can't access f anymore
        assert_eq!(name, "drop_test.txt");
    }
}
(* Drop and RAII concepts in OCaml *)

(* OCaml has finalizers, but RAII is done via explicit cleanup or with_* functions *)

type file_handle = {
  name: string;
  mutable closed: bool;
}

let open_file name =
  Printf.printf "Opening file: %s\n" name;
  { name; closed = false }

let close_file fh =
  if not fh.closed then begin
    Printf.printf "Closing file: %s\n" fh.name;
    fh.closed <- true
  end

(* RAII pattern via with_file *)
let with_file name f =
  let fh = open_file name in
  Fun.protect ~finally:(fun () -> close_file fh) (fun () -> f fh)

let () =
  (* Explicit cleanup *)
  let f = open_file "data.txt" in
  Printf.printf "Using file: %s\n" f.name;
  close_file f;

  (* RAII via with_file *)
  with_file "config.json" (fun f ->
    Printf.printf "Processing: %s\n" f.name
  )
  (* file automatically closed even if exception *)