m12-lifecycle
actionbook/rust-skills
Design resource creation, usage, and cleanup patterns using RAII, Drop, and lifecycle strategies.
What is m12-lifecycle?
This skill guides you through designing when and how resources are created, used, and cleaned up in Rust. Use it when building connection pools, implementing lazy initialization, managing transactions, or ensuring cleanup happens automatically on scope exit.
- Implement RAII patterns with Drop trait for automatic cleanup
- Design lazy initialization with OnceLock and LazyLock for deferred creation
- Plan connection pooling strategies for expensive resource reuse
- Apply guard patterns for scoped resource access and release
- Handle error cases to ensure cleanup happens even on failure
- Trace lifecycle decisions between domain constraints and implementation details
How to install m12-lifecycle
npx skills add https://github.com/actionbook/rust-skills --skill m12-lifecycleHow to use m12-lifecycle
- 1.Identify the resource cost (cheap vs. expensive) and scope (function-local, request-scoped, or application-wide)
- 2.Choose a lifecycle pattern: RAII for auto-cleanup, lazy init for deferred creation, pool for reuse, guard for scoped access, or scope for transactions
- 3.Implement the pattern using Drop trait, OnceLock/LazyLock, or connection pool libraries like r2d2 or deadpool
- 4.Verify cleanup happens on error paths by testing error cases
- 5.Trace up to domain constraints (latency, connection limits) and down to implementation details (ownership, concurrency)
Use cases
- Designing database connection pools with automatic return on drop
- Implementing lazy-loaded configuration singletons that initialize once
- Creating transaction scopes that commit or rollback on scope exit
- Building file handles that clean up temporary files automatically
- Managing mutex guards that release locks when leaving scope
- Backend engineers designing data access layers
- Systems programmers managing file and network resources
- Library authors implementing resource-safe abstractions
- Teams building connection pooling or caching layers
- Developers transitioning from manual memory management
m12-lifecycle FAQ
Use Drop for cleanup that must always happen (RAII). Use explicit close when cleanup is optional or can fail with errors that need handling.
OnceLock is thread-safe but requires manual initialization via get_or_init(). LazyLock is thread-safe and initializes automatically on first access.
Ensure Drop is implemented so connections are returned to the pool when guards go out of scope. Use connection timeouts and monitor pool metrics.
No, the compiler prevents moving owned fields. Use Option::take() to extract the value while leaving None in its place.
Return Result from explicit close methods. For Drop, use catch-all logging since Drop cannot return errors; consider separate fallible close() methods.
Full instructions (SKILL.md)
Source of truth, from actionbook/rust-skills.
name: m12-lifecycle description: "Use when designing resource lifecycles. Keywords: RAII, Drop, resource lifecycle, connection pool, lazy initialization, connection pool design, resource cleanup patterns, cleanup, scope, OnceCell, Lazy, once_cell, OnceLock, transaction, session management, when is Drop called, cleanup on error, guard pattern, scope guard, 资源生命周期, 连接池, 惰性初始化, 资源清理, RAII 模式" user-invocable: false
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<Config> = OnceLock::new();
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 |
Related skills
More from actionbook/rust-skills and the wider catalog.

m13-domain-error
Design domain error handling with categorization, recovery strategies, and resilience patterns.

m14-mental-model
Mental models and analogies for understanding Rust ownership, borrowing, and core concepts.

m15-anti-pattern
Identify and refactor Rust code anti-patterns during review.

meta-cognition-parallel
Three-layer parallel meta-cognition analysis for Rust questions—language mechanics, design choices, and domain constraints.

rust-call-graph
Visualize Rust function call graphs and trace caller/callee relationships using LSP.

rust-code-navigator
Navigate Rust code with LSP: go to definition, find references, and explore symbols efficiently.