AndyGauge/redrop

A proc-macro collector that closes the async-Drop-must-implement-Drop gap.

★ 0Forks 0RustGitHub ↗Compare

Project website ↗

README

redrop

redrop closes the "async Drop must implement Drop" gap. Rust's Drop::drop is synchronous, so a type that owns an async resource (a socket, a database handle, anything that needs .await to close cleanly) has no sanctioned way to run that cleanup when it goes out of scope.

#[redrop::async_drop] fixes that: annotate the async cleanup method, and the generated Drop::drop hands the cleanup future off to a collector instead of running it in place — one that handles the load of many things dropping at once, not just one at a time.

#[derive(Default)]
struct Connection {
    open: bool,
}

#[redrop::async_drop]
impl Connection {
    async fn async_drop(&mut self) {
        // self.socket.shutdown().await, etc.
        self.open = false;
    }
}

drop(Connection { open: true }); // queued, not run synchronously

// Before the process exits, drain the queue and wait for every collected drop to finish:
futures::executor::block_on(redrop::shutdown());

New to this? The tutorial site walks through it slowly, chapter by chapter, starting from why the problem exists at all.

Workspace layout

Crate What it is
redrop The library: the collector, shutdown(), set_concurrency_limit().
redrop-macros Proc-macro implementation for #[async_drop]. Not meant to be depended on directly.
showcase A small runnable demo crate — the tutorial's worked example.
site The Zola tutorial site.

Feature flags

  • default (no features) — runtime-agnostic. Cleanup futures queue up until you .await redrop::shutdown() on whatever executor you're using.
  • tokio — additionally spawns a background task (once it gets a chance to, inside a live Tokio runtime) that continuously drains the queue as drops happen, rather than only on shutdown().
[dependencies]
redrop = { version = "0.1", features = ["tokio"] }

shutdown() means the same thing either way: it's the guarantee that every queued and in-flight async drop has actually finished. Call it once, last, before your program exits.

Running things

# Library tests (both feature configurations)
cargo test -p redrop -p redrop-macros
cargo test --workspace --features tokio

# The showcase: drops a burst of values, reads its concurrency limit from showcase/redrop.toml
cargo run -p redrop-showcase

# Same, but pointed at a different config file
REDROP_CONFIG=/path/to/other.toml cargo run -p redrop-showcase

# The load example, under the tokio feature
cargo run -p redrop --example load --features tokio

The tutorial site

Built with Zola. A few make targets wrap the commands:

make doc-serve   # zola serve, with live reload, for editing chapters
make doc         # zola build -> site/public
make doc-check   # validate templates/links without writing output
make doc-clean    # remove site/public

CI

.github/workflows/ci.yml runs on every push/PR:

  • test — cargo test/clippy (-D warnings) across both feature configurations.
  • build-site — validates the tutorial site builds cleanly.
  • deploy — on pushes to main only: builds the site with the real Pages base URL and publishes it via GitHub Pages.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Contributors

AndyGauge

Issues