async-dropper is probably the least-worst ad-hoc AsyncDrop implementation you've seen, and it works in two ways:
async_dropper::AsyncDropperis stolen nearly verbatim from this StackOverflow answer (thanks topaholg!)async_dropper::AsyncDropis a Trait and custom derive macro, which try to useDefaultandPartialEqto determine when to async drop, automatically whileDroping.
The code in this crate was most directly inspired by this StackOverflow thread on Async Drop and many other conversations:
- Async Drop? - Reddit
- Asynchronous Destructors - rust-lang.org
- Async Drop roadmap (once this is done, this crate will be deprecated!)
Install
You must set features on this crate, as it works with both async runtimes, and can use either approach outlined above:
# if using tokio, choose one of the two lines below
cargo add async-dropper --features tokio,derive # use tokio, with the derive approach
cargo add async-dropper --features tokio,simple # use tokio, with the simple approach
# if using async-std, chose one of the two lines below
cargo add async-dropper --features async-std,derive # use async-std, with the derive approach
cargo add async-dropper --features async-std,simple # use async-std, with the simple approach
If you're editing Cargo.toml by hand, choose one of the following lines:
[]
#async-dropper = { version = "0.3", features = [ "tokio", "derive" ] }
#async-dropper = { version = "0.3", features = [ "tokio", "simple" ] }
#async-dropper = { version = "0.3", features = [ "async-std", "derive" ] }
#async-dropper = { version = "0.3", features = [ "async-std", "simple" ] }
Warning
async-dropperdoes not allow using bothasync-stdandtokiofeatures at the same time (see the FAQ below). You can, however, use both thesimpleandderivefeatures at the same time
Quickstart
async_dropper::simple
To use the "simple" version which uses a wrapper struct (AsyncDropper<T>), see examples/simple_tokio.rs:
use ;
use ;
use async_trait;
// NOTE: this example is rooted in crates/async-dropper
/// This object will be async-dropped (which must be wrapped in AsyncDropper)
;
async
You can run the example and see the output:
cargo run --example simple-tokio --features=tokio
async_dropper::derive
The derive macro is a novel (and possibly foolhardy) attempt to implement AsyncDrop without actually wrapping the existing struct.
async_dropper::derive uses Default and PartialEq to check if the struct in question is equivalent to it's default.
For this approach to work well your T should have cheap-to-create Defaults, and comparing a default value to an existing value should meaningfully differ (and identify an object that is no longer in use). Please think thoroughly about whether this model works for your use case.
For an example, see examples/derive_tokio.rs:
use ;
use AsyncDrop;
use async_trait;
/// This object will be async-dropped
///
/// Objects that are dropped *must* implement [Default] and [PartialEq]
/// (so make members optional, hide them behind Rc/Arc as necessary)
;
/// Implementation of [AsyncDrop] that specifies the actual behavior
async
You can run the example and see the output:
cargo run --example derive-tokio --features=tokio
Supported environments
async-dropper works with the following async environments:
| Name | Supported? |
|---|---|
Async w/ tokio |
✅ |
Async w/ async-std |
✅ |
FAQ
Why does async-dropper assume that I'm using either async-std or tokio?
Because you probably are. If this is a problem for you, it can be changed, please file an issue.
Why do I have to choose between simple and derive features?
The simple strategy and derive strategy impose different requirements on the T on which they act.
To avoid requiring unnnecessary and possibly incompatible traits, you should choose one of the features (i.e. approaches) to go with.
If this "feature" presents an issue for you, it can be changed, please file an issue.
What does async_dropper::derive cost?
There is waste introduced by async_dropper::derive, namely:
- One
Mutex-protectedT::default()instance of your type, that exists as long as the program runs - One extra
T::default()that is made of an individualTbeing dropped.
As a result, every drop you perform on a T will perform two drops -- one on a T::default() and another on your T, which has been converted to a T::default (via reset(&mut self)).
Development
To get started working on developing async-dropper, run the following just targets:
just setup build
To check that your changes are fine, you'll probably want to run:
just test
If you want to see the full list of targets available that you can run just without any arguments.
just
There are a few useful targets like just build-watch which will continuously build the project thanks to cargo watch.
Releasing
From the top level of this repository:
PUBLISH_CRATE=yes PKG=<crate name> just release <version>
For example, to create the next semver patch release for async-dropper-simple:
PUBLISH_CRATE=yes PKG=async-dropper-simple just release patch
Contributing
Contributions are welcome! If you find a bug or an impovement that should be included in async-dropper, [create an issue][crate-issue] or open a pull request.