{"id":13636429,"url":"https://github.com/t3hmrman/async-dropper","last_synced_at":"2025-12-25T19:28:04.617Z","repository":{"id":184577530,"uuid":"670943551","full_name":"t3hmrman/async-dropper","owner":"t3hmrman","description":"🗑 async-dropper is probably the least-worst ad-hoc AysncDrop implementation you've seen so far.","archived":false,"fork":false,"pushed_at":"2024-11-28T07:13:40.000Z","size":215,"stargazers_count":50,"open_issues_count":0,"forks_count":6,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-05T08:25:09.588Z","etag":null,"topics":["async","rust","rust-lang","utility-library"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/async-dropper","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/t3hmrman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-07-26T07:28:37.000Z","updated_at":"2025-07-17T14:24:59.000Z","dependencies_parsed_at":null,"dependency_job_id":"6349a5c5-abc1-48ab-99bf-8d470aa20b7f","html_url":"https://github.com/t3hmrman/async-dropper","commit_stats":null,"previous_names":["t3hmrman/async-dropper"],"tags_count":40,"template":false,"template_full_name":null,"purl":"pkg:github/t3hmrman/async-dropper","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t3hmrman%2Fasync-dropper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t3hmrman%2Fasync-dropper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t3hmrman%2Fasync-dropper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t3hmrman%2Fasync-dropper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/t3hmrman","download_url":"https://codeload.github.com/t3hmrman/async-dropper/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t3hmrman%2Fasync-dropper/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28035508,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-25T02:00:05.988Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","rust","rust-lang","utility-library"],"created_at":"2024-08-02T00:01:01.148Z","updated_at":"2025-12-25T19:28:04.595Z","avatar_url":"https://github.com/t3hmrman.png","language":"Rust","funding_links":[],"categories":["Libraries"],"sub_categories":["Asynchronous"],"readme":"\u003ch1 align=\"center\"\u003e🗑  \u003ccode\u003easync-dropper\u003c/code\u003e\u003c/h1\u003e\n\n`async-dropper` is probably the least-worst ad-hoc `AsyncDrop` implementation you've seen, and it works in two ways:\n\n- `async_dropper::AsyncDropper` is stolen nearly verbatim from [this StackOverflow answer](https://stackoverflow.com/a/75584109) (thanks to [`paholg`](https://stackoverflow.com/users/2977291/paholg)!)\n- `async_dropper::AsyncDrop` is a Trait and [custom derive macro][rust-derive-macro], which try to use `Default` and `PartialEq` to determine when to async drop, automatically while `Drop`ing.\n\nThe code in this crate was most directly inspired by [this StackOverflow thread on Async Drop](https://stackoverflow.com/questions/71541765/rust-async-drop) and many other conversations:\n\n- [Async Drop? - Reddit](https://www.reddit.com/r/rust/comments/vckd9h/async_drop/)\n- [Asynchronous Destructors - rust-lang.org](https://internals.rust-lang.org/t/asynchronous-destructors/11127)\n- [Async Drop roadmap](https://rust-lang.github.io/async-fundamentals-initiative/roadmap/async_drop.html) (once this is done, this crate will be deprecated!)\n\n[rust-derive-macro]: https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros\n\n## Install\n\nYou **must** set features on this crate, as it works with both async runtimes, and can use *either* approach outlined above:\n\n```console\n# if using tokio, choose one of the two lines below\ncargo add async-dropper --features tokio,derive     # use tokio, with the derive approach\ncargo add async-dropper --features tokio,simple     # use tokio, with the simple approach\n\n# if using async-std, chose one of the two lines below\ncargo add async-dropper --features async-std,derive # use async-std, with the derive approach\ncargo add async-dropper --features async-std,simple # use async-std, with the simple approach\n```\n\nIf you're editing `Cargo.toml` by hand, **choose one** of the following lines:\n\n```toml\n[dependencies]\n#async-dropper = { version = \"0.3\", features = [ \"tokio\", \"derive\" ] }\n#async-dropper = { version = \"0.3\", features = [ \"tokio\", \"simple\" ] }\n\n#async-dropper = { version = \"0.3\", features = [ \"async-std\", \"derive\" ] }\n#async-dropper = { version = \"0.3\", features = [ \"async-std\", \"simple\" ] }\n```\n\n\u003e **Warning**\n\u003e `async-dropper` does not allow using both `async-std` and `tokio` features at the same time (see [the FAQ below](#FAQ)).\n\u003e You *can*, however, use both the `simple` and `derive` features at the same time\n\n### Other features\n\nHere are some other features you might be interested in using:\n\n| Crate           | Feature  | Description                                                                  |\n|-----------------|----------|------------------------------------------------------------------------------|\n| `async-dropper` | `anyhow` | Enable [`anyhow`][crate-anyhow]-related functionality (ex. `From` instances) |\n\n[crate-anyhow]: https://crates.io/crates/anyhow\n\n## Quickstart\n\n### `async_dropper::simple`\n\nTo use the \"simple\" version which uses a wrapper struct (`AsyncDropper\u003cT\u003e`), see [`examples/simple_tokio.rs`](./examples/simple_tokio.rs):\n\n```rust\nuse std::{\n    result::Result,\n    time::Duration,\n};\n\nuse async_dropper_simple::{AsyncDrop, AsyncDropper};\nuse async_trait::async_trait;\n\n// NOTE: this example is rooted in crates/async-dropper\n\n/// This object will be async-dropped (which must be wrapped in AsyncDropper)\n#[derive(Default)]\nstruct AsyncThing(String);\n\n#[async_trait]\nimpl AsyncDrop for AsyncThing {\n    async fn async_drop(\u0026mut self) {\n        eprintln!(\"async dropping [{}]!\", self.0);\n        tokio::time::sleep(Duration::from_secs(2)).await;\n        eprintln!(\"dropped [{}]!\", self.0);\n    }\n}\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    {\n        let _example_obj = AsyncDropper::new(AsyncThing(String::from(\"test\")));\n        eprintln!(\"here comes the (async) drop\");\n        // drop will be triggered here, and it will take *however long it takes*\n        // you could also call `drop(_example_obj)`\n    }\n\n    Ok(())\n}\n\n```\n\nYou can run the example and see the output:\n\n```console\ncargo run --example simple-tokio --features=tokio\n```\n\n### Don't like the `Default` requirement?\n\nIt [was suggested](https://github.com/t3hmrman/async-dropper/issues/12#issuecomment-1913642636) that for certain large structs, it may not be convenient to implement `Default` in order to use the simple `AsyncDropper`.\n\nAs of version `0.2.6`, `async-dropper-simple` has a feature flag called `no-default-bound` which allows you to skip the `Default` bound on your `T` (in `AsyncDropper\u003cT\u003e`), by using an inner `Option\u003cT\u003e` (thanks @beckend!).\n\n### `async_dropper::derive`\n\nThe derive macro is a novel (and possibly foolhardy) attempt to implement `AsyncDrop` without actually wrapping the existing struct.\n\n`async_dropper::derive` uses `Default` and `PartialEq` to *check if the struct in question is equivalent to it's default*.\n\nFor this approach to work well your `T` should have cheap-to-create `Default`s, 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**.\n\nFor an example, see [`examples/derive_tokio.rs`](./examples/derive_tokio.rs):\n\n```rust\nuse std::{\n    result::Result,\n    time::Duration,\n};\n\nuse async_dropper::derive::AsyncDrop;\nuse async_trait::async_trait;\n\n/// This object will be async-dropped\n///\n/// Objects that are dropped *must* implement [Default] and [PartialEq]\n/// (so make members optional, hide them behind Rc/Arc as necessary)\n#[derive(Debug, Default, PartialEq, Eq, AsyncDrop)]\nstruct AsyncThing(String);\n\n/// Implementation of [AsyncDrop] that specifies the actual behavior\n#[async_trait]\nimpl AsyncDrop for AsyncThing {\n    // simulated work during async_drop\n    async fn async_drop(\u0026mut self) -\u003e Result\u003c(), AsyncDropError\u003e {\n        eprintln!(\"async dropping [{}]!\", self.0);\n        tokio::time::sleep(Duration::from_secs(2)).await;\n        eprintln!(\"dropped [{}]!\", self.0);\n        Ok(())\n    }\n\n    fn drop_timeout(\u0026self) -\u003e Duration {\n        Duration::from_secs(5) // extended from default 3 seconds, as an example\n    }\n\n    // NOTE: the method below is automatically derived for you, but you can override it\n    // make sure that the object is equal to T::default() by the end, otherwise it will panic!\n    // fn reset(\u0026mut self) {\n    //     self.0 = String::default();\n    // }\n\n    // NOTE: below was not implemented since we want the default of DropFailAction::Continue\n    // fn drop_fail_action(\u0026self) -\u003e DropFailAction;\n}\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    {\n        let _example_obj = AsyncThing(String::from(\"test\"));\n        eprintln!(\"here comes the (async) drop\");\n        // drop will be triggered here\n        // you could also call `drop(_example_obj)`\n    }\n\n    Ok(())\n}\n```\n\nYou can run the example and see the output:\n\n```console\ncargo run --example derive-tokio --features=tokio\n```\n\n## Supported environments\n\n`async-dropper` works with the following async environments:\n\n| Name                              | Supported? |\n|-----------------------------------|------------|\n| Async w/ [`tokio`][tokio]         | ✅         |\n| Async w/ [`async-std`][async-std] | ✅         |\n\n[tokio]: https://crates.io/crates/tokio\n[async-std]: https://crates.io/crates/async-std\n\n## FAQ\n\n### Why does `async-dropper` assume that I'm using *either* `async-std` or `tokio`?\n\nBecause you probably are. If this is a problem for you, it *can* be changed, please [file an issue][create-issue].\n\n### Why do I have to choose between `simple` and `derive` features?\n\nThe `simple` strategy and `derive` strategy impose different requirements on the `T` on which they act.\n\nTo avoid requiring unnnecessary and possibly incompatible traits, you should choose *one* of the features (i.e. approaches) to go with.\n\nIf this \"feature\" presents an issue for you, it *can* be changed, please [file an issue][create-issue].\n\n### What does `async_dropper::derive` cost?\n\nThere is waste introduced by `async_dropper::derive`, namely:\n\n- One `Mutex`-protected `T::default()` instance of your type, that exists as long as the program runs\n- One extra `T::default()` that is made of an individual `T` being dropped.\n\nAs 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(\u0026mut self)`).\n\n## Development\n\nTo get started working on developing `async-dropper`, run the following [`just`][just] targets:\n\n```console\njust setup build\n```\n\nTo check that your changes are fine, you'll probably want to run:\n\n```console\njust test\n```\n\nIf you want to see the full list of targets available that you can run `just` without any arguments.\n\n```console\njust\n```\n\nThere are a few useful targets like `just build-watch` which will continuously build the project thanks to [`cargo watch`][cargo-watch].\n\n[just]: https://github.com/casey/just\n[cargo-watch]: https://crates.io/crates/cargo-watch\n\n## Releasing\n\nFrom the top level of this repository:\n\n```console\nPUBLISH_CRATE=yes PKG=\u003ccrate name\u003e just release \u003cversion\u003e\n```\n\nFor example, to create the next semver `patch` release for `async-dropper-simple`:\n\n```console\nPUBLISH_CRATE=yes PKG=async-dropper-simple just release patch\n```\n\n## Contributing\n\nContributions 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.\n\n[create-issue]: https://github.com/t3hmrman/async-dropper/issues\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ft3hmrman%2Fasync-dropper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ft3hmrman%2Fasync-dropper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ft3hmrman%2Fasync-dropper/lists"}