PluginBench
Skill
Pass
Audit score 90

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-lifecycle
Claude Code
Cursor
Windsurf
Cline

How to use m12-lifecycle

  1. 1.Identify the resource cost (cheap vs. expensive) and scope (function-local, request-scoped, or application-wide)
  2. 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. 3.Implement the pattern using Drop trait, OnceLock/LazyLock, or connection pool libraries like r2d2 or deadpool
  4. 4.Verify cleanup happens on error paths by testing error cases
  5. 5.Trace up to domain constraints (latency, connection limits) and down to implementation details (ownership, concurrency)

Use cases

Good for
  • 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
Who it's for
  • 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

When should I use Drop vs. explicit close methods?

Use Drop for cleanup that must always happen (RAII). Use explicit close when cleanup is optional or can fail with errors that need handling.

What's the difference between OnceLock and LazyLock?

OnceLock is thread-safe but requires manual initialization via get_or_init(). LazyLock is thread-safe and initializes automatically on first access.

How do I prevent connection pool exhaustion?

Ensure Drop is implemented so connections are returned to the pool when guards go out of scope. Use connection timeouts and monitor pool metrics.

Can I move fields out of a struct that implements Drop?

No, the compiler prevents moving owned fields. Use Option::take() to extract the value while leaving None in its place.

How do I handle cleanup failures?

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

PatternWhenImplementation
RAIIAuto cleanupDrop trait
Lazy initDeferred creationOnceLock, LazyLock
PoolReuse expensive resourcesr2d2, deadpool
GuardScoped accessMutexGuard pattern
ScopeTransaction boundaryCustom struct + Drop

Thinking Prompt

Before designing lifecycle:

  1. What's the resource cost?

    • Cheap → create per use
    • Expensive → pool or cache
    • Global → lazy singleton
  2. What's the scope?

    • Function-local → stack allocation
    • Request-scoped → passed or extracted
    • Application-wide → static or Arc
  3. 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)
QuestionTrace ToAsk
Connection poolingdomain-*What's acceptable latency?
Resource limitsdomain-*What are infra constraints?
Transaction scopedomain-*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

PatternTypeUse Case
RAIIDrop traitAuto cleanup on scope exit
Lazy InitOnceLock, LazyLockDeferred initialization
Poolr2d2, deadpoolConnection reuse
GuardMutexGuardScoped lock release
ScopeCustom structTransaction boundaries

Lifecycle Events

EventRust Mechanism
Creationnew(), Default
Lazy InitOnceLock::get_or_init
Usage&self, &mut self
CleanupDrop::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

ErrorCauseFix
Resource leakForgot DropImplement Drop or RAII wrapper
Double freeManual memoryLet Rust handle
Use after dropDangling referenceCheck lifetimes
E0509 move out of DropMoving owned fieldOption::take()
Pool exhaustionNot returnedEnsure Drop returns

Anti-Patterns

Anti-PatternWhy BadBetter
Manual cleanupEasy to forgetRAII/Drop
lazy_static!External depstd::sync::OnceLock
Global mutable stateThread unsafetyOnceLock or proper sync
Forget to closeResource leakDrop impl

Related Skills

WhenSee
Smart pointersm02-resource
Thread-safe initm07-concurrency
Domain scopesm09-domain
Error in cleanupm06-error-handling