Drop and Resource Management

RAII

RAII (Resource Acquisition Is Initialization, also known as Scope-Bound Resource Management or SBRM) is the idiom in which a resource's lifetime is bound strictly to the lifetime of a stack-allocated object: the constructor acquires the resource and establishes invariants, and the destructor (Drop::drop) releases the resource when the object goes out of scope.

Core Principles of RAII in Rust

  1. No half-initialized states: Constructors (File::open, Mutex::lock, TcpStream::connect) return Result<Guard, Error> rather than an uninitialized object that requires a separate .init() call. If acquisition fails, no object is constructed and no destructor runs.
  2. Access mediated through the guard: The resource can only be used through methods or Deref on the RAII object, making use-after-close or unlocked access impossible at compile time.
  3. Move semantics prevent double release: Because Rust moves values by default (and forbids Copy on types implementing Drop), transferring an RAII handle to another function or thread transfers responsibility for running the destructor without duplicating the handle.
RAII Lifecycle:
  Constructor (`acquire()`)
      |
      +-- Failure -> Returns `Err(E)` (No guard created, no Drop needed)
      |
      +-- Success -> Returns `Ok(Guard)`
                         |
                         v
                Guard In Scope (Resource active & accessible)
                         |
                         v
                Scope Exit / Unwind -> `Guard::drop(&mut self)` releases resource

Standard Library RAII Guards

RAII Type Acquisition Function Resource Released in Drop
Box<T>, Vec<T>, String Box::new, Vec::with_capacity Heap memory via GlobalAlloc::dealloc
std::fs::File File::open, File::create Operating system file descriptor (close / CloseHandle)
MutexGuard<'_, T> Mutex::lock, Mutex::try_lock Mutex lock state
RwLockReadGuard<'_, T>, RwLockWriteGuard<'_, T> RwLock::read, RwLock::write Reader or writer lock state
Ref<'_, T>, RefMut<'_, T> RefCell::borrow, RefCell::borrow_mut Dynamic borrow flag in RefCell<T>
PeekMut<'_, T> BinaryHeap::peek_mut Sifts modified top element back into heap order

Building a Custom RAII Guard

A custom RAII guard encapsulates a setup action in its constructor and a guaranteed teardown action in its Drop implementation—even if intermediate operations return early or panic:

use std::cell::Cell;

struct IndentationGuard<'a> {
    depth: &'a Cell<usize>,
}

impl<'a> IndentationGuard<'a> {
    fn enter(depth: &'a Cell<usize>, section: &str) -> Self {
        let current = depth.get();
        println!("{:indent$}<{}>", "", section, indent = current * 2);
        depth.set(current + 1);
        Self { depth }
    }
}

impl Drop for IndentationGuard<'_> {
    fn drop(&mut self) {
        let next = self.depth.get() - 1;
        self.depth.set(next);
        println!("{:indent$}</>", "", indent = next * 2);
    }
}

fn main() {
    let depth = Cell::new(0);

    let _root = IndentationGuard::enter(&depth, "html");
    {
        let _body = IndentationGuard::enter(&depth, "body");
        {
            let _p = IndentationGuard::enter(&depth, "p");
            println!("{:indent$}Hello, RAII!", "", indent = depth.get() * 2);
        }
    }
}
<html>
  <body>
    <p>
      Hello, RAII!
    </>
  </>
</>