RAII & Drop Guards
Resource Acquisition Is Initialization: acquire resources in constructors, release in Drop when scope ends. Scope guards run cleanup even on panic (with unwinding).
Recipe
struct TempDirGuard(tempfile::TempDir);
impl Drop for TempDirGuard {
fn drop(&mut self) { /* tempdir deletes on drop */ }
}When to reach for this: Locks, files, transactions, metric timers, restoring previous state.
Working Example
struct ResetFlag<'a> { flag: &'a AtomicBool, prev: bool }
impl<'a> ResetFlag<'a> {
fn guard(flag: &'a AtomicBool, value: bool) -> Self {
let prev = flag.swap(value, Ordering::SeqCst);
ResetFlag { flag, prev }
}
}
impl Drop for ResetFlag<'_> {
fn drop(&mut self) {
self.flag.store(self.prev, Ordering::SeqCst);
}
}
fn work() {
let _g = ResetFlag::guard(&IN_PROGRESS, true);
// flag restored when _g drops
}What this demonstrates:
_gbinding keeps guard alive until scope end- Drop runs on normal return and panic (unwinding)
- Encapsulate restore logic once
scopeguardcrate for closure-based guards
Deep Dive
defer pattern with scopeguard::guard((), |_| cleanup()). Mutex guards (MutexGuard) are RAII locks. tokio::spawn tasks should use RAII for in-flight counters.
Gotchas
- Drop order - fields dropped in declaration reverse order. Fix: structure fields if order matters.
- panic=abort - no unwinding drop in some builds. Fix: critical cleanup may need
finallystyle explicit paths too for abort builds. - async Drop - not automatic. Fix: explicit shutdown future or
Droponly for sync resources. - Forgotten
_bind - guard dropped immediately. Fix:let _guard = ...for full scope. - Deadlock on drop - lock order in nested guards. Fix: consistent lock ordering.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
try/finally manual | abort builds critical path | Normal Rust RAII |
scopeguard crate | Quick closure guard | Custom type clearer |
Explicit close() | Async shutdown | Sync file handles |
FAQs
Drop on panic?
Yes with unwinding; use catch_unwind at boundary if needed.
Manually drop early?
drop(guard) before scope end.
Drop and Copy?
Copy types can implement Drop with care; rare pattern.
union Drop order fields?
Use nested guards or single struct owning multiple resources.
log in Drop?
Avoid logging in Drop that may panic; keep Drop infallible.
transaction rollback?
DB transaction guard rolls back unless committed.
metrics timer guard?
Record duration in Drop of timer struct.
forget guard?
mem::forget leaks resource intentionally; document why.
Send/Sync guard?
Guard fields determine auto traits; watch RefCell in guard.
test Drop?
Use flag atomics or counters asserted after scope.
Related
- Smart Pointers Basics - Drop trait
- Mutex and RwLock - lock guards
- Common Anti-Patterns - leak patterns
- 30 Unsafe Rules
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.