async-drop

cryfs's avatarfrom cryfs

Guide to the AsyncDrop pattern for async cleanup in Rust. Use when working with AsyncDropGuard, implementing AsyncDrop trait, or handling async resource cleanup.

⭐2.2kstars🔀169forks📁View on GitHub🕐Updated Dec 31, 2025

When & Why to Use This Skill

This Claude skill provides a comprehensive guide to the AsyncDrop pattern in Rust, a critical architectural solution for managing asynchronous resource cleanup. It addresses the limitations of Rust's native synchronous Drop trait by providing structured implementation patterns, guards, and macros to ensure network connections, file handles, and background tasks are closed safely and efficiently without resource leaks.

Use Cases

  • Graceful Shutdown of Network Resources: Implementing asynchronous teardown for web sockets, database connections, and API clients that require an awaitable closing sequence.
  • Asynchronous File and Transaction Management: Ensuring file buffers are flushed and database transactions are committed or rolled back asynchronously before a resource is reclaimed.
  • Background Task Orchestration: Managing the lifecycle of background workers and ensuring they are properly joined or terminated when their parent structure is dropped.
  • Robust System Resource Handling: Preventing resource leaks in high-performance Rust applications by enforcing strict async cleanup rules through the AsyncDropGuard and specialized macros.
nameasync-drop
descriptionGuide to the AsyncDrop pattern for async cleanup in Rust. Use when working with AsyncDropGuard, implementing AsyncDrop trait, or handling async resource cleanup.

AsyncDrop Pattern Guide

The AsyncDrop pattern enables async cleanup for types that hold resources requiring asynchronous teardown (network connections, file handles, background tasks, etc.).

Core Concept

Rust's Drop trait is synchronous, but sometimes cleanup needs to be async. The AsyncDrop pattern solves this by:

  1. Wrapping values in AsyncDropGuard<T>
  2. Requiring explicit async_drop().await calls
  3. Panicking if cleanup is forgotten

Quick Reference

// Creating
let mut guard = AsyncDropGuard::new(my_value);

// Using (transparent via Deref)
guard.do_something();

// Cleanup (REQUIRED before dropping)
guard.async_drop().await?;

The AsyncDrop Trait

#[async_trait]
pub trait AsyncDrop {
    type Error: Debug;
    async fn async_drop_impl(&mut self) -> Result<(), Self::Error>;
}

Essential Rules

Rule Description
Always call async_drop() Every AsyncDropGuard must have async_drop() called
Factory methods return guards fn new() -> AsyncDropGuard<Self>, never plain Self
Types with guard members impl AsyncDrop Delegate to member async_drops
Use the macro when possible with_async_drop_2! handles cleanup automatically
Panics are exceptions It's OK to skip async_drop on panic paths

The with_async_drop_2! Macro

Automatically calls async_drop() on scope exit:

let resource = get_resource().await?;
with_async_drop_2!(resource, {
    // Use resource here
    resource.do_work().await?;
    Ok(result)
})

Additional References

  • patterns.md - Implementation patterns and examples
  • gotchas.md - Common mistakes and how to avoid them
  • helpers.md - Helper types (AsyncDropArc, AsyncDropHashMap, etc.)

Location

Implementation: crates/utils/src/async_drop/