Resource Lifecycle
Layer 2: Design Choices
Core Question
When should this resource be created, used, and cleaned up?
Before implementing lifecycle:
What's the resource's scope? Who owns the cleanup responsibility? What happens on error? Lifecycle Pattern → Implementation Pattern When Implementation RAII Auto cleanup Drop trait Lazy init Deferred creation OnceLock, LazyLock Pool Reuse expensive resources r2d2, deadpool Guard Scoped access MutexGuard pattern Scope Transaction boundary Custom struct + Drop Thinking Prompt
Before designing lifecycle:
What's the resource cost?
Cheap → create per use Expensive → pool or cache Global → lazy singleton
What's the scope?
Function-local → stack allocation Request-scoped → passed or extracted Application-wide → static or Arc
What about errors?
Cleanup must happen → Drop Cleanup is optional → explicit close Cleanup can fail → Result from close Trace Up ↑
To domain constraints (Layer 3):
"How should I manage database connections?" ↑ Ask: What's the connection cost? ↑ Check: domain-* (latency requirements) ↑ Check: Infrastructure (connection limits)
Question Trace To Ask Connection pooling domain- What's acceptable latency? Resource limits domain- What are infra constraints? Transaction scope domain-* What must be atomic? Trace Down ↓
To implementation (Layer 1):
"Need automatic cleanup" ↓ m02-resource: Implement Drop ↓ m01-ownership: Clear owner for cleanup
"Need lazy initialization" ↓ m03-mutability: OnceLock for thread-safe ↓ m07-concurrency: LazyLock for sync
"Need connection pool" ↓ m07-concurrency: Thread-safe pool ↓ m02-resource: Arc for sharing
Quick Reference Pattern Type Use Case RAII Drop trait Auto cleanup on scope exit Lazy Init OnceLock, LazyLock Deferred initialization Pool r2d2, deadpool Connection reuse Guard MutexGuard Scoped lock release Scope Custom struct Transaction boundaries Lifecycle Events Event Rust Mechanism Creation new(), Default Lazy Init OnceLock::get_or_init Usage &self, &mut self Cleanup Drop::drop() Pattern Templates RAII Guard struct FileGuard { path: PathBuf, _handle: File, }
impl Drop for FileGuard { fn drop(&mut self) { // Cleanup: remove temp file let _ = std::fs::remove_file(&self.path); } }
Lazy Singleton use std::sync::OnceLock;
static CONFIG: OnceLock
fn get_config() -> &'static Config { CONFIG.get_or_init(|| { Config::load().expect("config required") }) }
Common Errors Error Cause Fix Resource leak Forgot Drop Implement Drop or RAII wrapper Double free Manual memory Let Rust handle Use after drop Dangling reference Check lifetimes E0509 move out of Drop Moving owned field Option::take() Pool exhaustion Not returned Ensure Drop returns Anti-Patterns Anti-Pattern Why Bad Better Manual cleanup Easy to forget RAII/Drop lazy_static! External dep std::sync::OnceLock Global mutable state Thread unsafety OnceLock or proper sync Forget to close Resource leak Drop impl Related Skills When See Smart pointers m02-resource Thread-safe init m07-concurrency Domain scopes m09-domain Error in cleanup m06-error-handling