CLI tool
est31/cargo-udeps avatar
est31/cargo-udeps

cargo-udeps: finds unused Cargo.toml dependencies, and needs nightly to do it

Find unused dependencies in Cargo.toml

2,143 stars53 forksRustNOASSERTION

At a glance

What is it?
A Cargo subcommand that reports declared-but-unreferenced crates. Accurate enough to be worth trusting, with two documented blind spots you will hit immediately.
Who is it for?
cargo-udeps is the right tool when you suspect a `Cargo.toml` has accumulated dependencies nobody removed, and the wrong tool when you want a fast check on every commit. It needs `cargo +nightly udeps` rather than a stable toolchain, and it inspects a nightly-instrumented build, so it is slower than a metadata query.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 162 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 7, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Why it needs nightly while its own build does not

The README makes a distinction worth reading twice: compilation of the tool itself works on Rust stable, but running it needs Rust nightly. That is unusual enough to be a design fact rather than a caveat. The reason is structural. `cargo-udeps` includes `cargo` itself as a dependency, which the README acknowledges makes it likely to compile against the latest rustc release and the one before it.

The dependency list in `Cargo.toml` shows how small the tool is in terms of what it adds on top of cargo: `cargo = "0.96.0"`, `cargo-util`, `serde` with derive, `serde_json`, `clap` with derive, `anyhow` and `nu-ansi-term` for terminal colouring. Two feature flags exist for vendoring, `vendored-openssl` and `vendored-libgit2`, both forwarding to the same-named cargo features, which is what you would reach for on a machine without a system OpenSSL or libgit2.

The `edition = "2024"` key tells you this crate tracks current Rust releases closely. The dev profile sets `debug = false`, and the release profile turns on LTO, forces `codegen-units = 1`, sets `panic = "abort"` and strips symbols. None of that affects how you use the tool, but it does mean the published binary is optimised for size and distribution rather than for your local experiments.

Four ways to install it, and which one to use

The install section lists four routes. GitHub Releases is listed first and links the releases page. From crates.io it is a single command:

bash
cargo install cargo-udeps --locked

The `--locked` flag is not incidental. It installs the versions pinned in the published `Cargo.lock` rather than resolving fresh, which matters for a tool whose whole job is reading dependency graphs.

If you want the unreleased state of the `master` branch instead, the same command with a git source does it:

bash
cargo install --git https://github.com/est31/cargo-udeps --locked

Third-party packaging exists too, which tells you the tool is common enough to be worth packaging. Nix and NixOS have `cargo-udeps`, Arch Linux offers `pacman -S cargo-udeps`, and Homebrew has `brew install cargo-udeps`. On macOS the Homebrew route avoids compiling it at all.

One inconsistency to keep in mind: the pre-commit example in the README pins `rev: v0.1.47`, while the current release line is well past that. If you copy the pre-commit snippet verbatim you are installing an old version, so update the revision to whatever release you actually depend on.

Running it, and what the output actually looks like

Usage is a Cargo subcommand with the nightly toolchain explicitly selected:

bash
cargo +nightly udeps

The README describes exactly two possible outputs. Either it prints an unused crates line listing the crates it found, or it prints a line saying no crates were unused. There is no severity threshold, no auto-fix, and no diff against a baseline, which is the right amount of ambition for a tool that reports a fact.

It is available as a pre-commit hook, so you can fail a commit on a newly introduced unused dependency rather than discovering it at release time:

yaml
- repo: https://github.com/est31/cargo-udeps
  rev: v0.1.47
  hooks:
  - id: udeps

Two practical consequences follow from how the tool works. It has to build your crate, and it has to do so under nightly instrumentation, so expect it to be slower than `cargo build` and much slower than reading a manifest. And because it needs nightly, CI needs a nightly toolchain installed for that job, which is a real cost if your pipeline standardises on stable.

Suppressing false positives with metadata keys in Cargo.toml

The escape hatch is a table in `Cargo.toml` under `package.metadata.cargo-udeps.ignore`, keyed by dependency kind:

toml
[package.metadata.cargo-udeps.ignore]
normal = ["if_chain"]
#development = []
#build = []

[dependencies]
if_chain = "1.0.0" # Used only in doc-tests, which `cargo-udeps` cannot check.

The three keys are `normal`, `development` and `build`, matching Cargo's dependency kinds. The worked example in the README is a dependency used only in doc-tests, which is exactly the category of thing the tool cannot see, and it leaves the two unused keys commented out.

In a workspace you can put the same configuration in the root manifest as `workspace.metadata.cargo-udeps.ignore` and it applies to every package in the workspace. That is the version to reach for when the same crate appears as an unused dependency in twenty member crates and you have accepted that it is genuinely used.

Be deliberate about the ignore list, though. It is easy to add a name once and never remove it, and then the tool keeps reporting clean on a dependency that has since become unused. Treat the file as a list of things you have checked, not a suppression backlog.

Two documented blind spots and a name-based limitation

The Known bugs section is short and specific, which makes it the most useful part of the README. Three limitations are named.

Some unused crates might not be detected. The two categories called out are crates used by `std` and its dependencies, and crates that are already being used by dependencies of the crate being studied. The second one is the counterintuitive case: if two of your dependencies both pull in `log`, then your direct `log` entry looks unused even though removing it would change your crate's API surface. Expect this and treat those reports as questions rather than instructions.

Crates are handled on a per-name basis. Two crates sharing a name at different versions would be a problem, which matters in workspaces that pull multiple majors of the same library.

So the tool reports a subset of unused dependencies, and the subset it misses are the ones a human would argue about. That is a defensible design, because the remaining reports are the actionable ones. The README also collects a trophy case of real pull requests and commits where the tool found unused dependencies, including ones in nushell, rust-lang/crater, just and yew_router, which is the closest thing here to evidence about how well it works on real projects.

Where cargo-udeps sits among the other Cargo tidy-up tools

Search interest around this tool clusters on comparison, which suggests the ecosystem has several answers to the same question. Alongside cargo-udeps, people look at cargo-machete, cargo-shear, cargo-unused-features and cargo-prune. The README itself does not discuss any of them, so the honest comparison has to stop at what this repository states.

What cargo-udeps commits to is a nightly requirement and an instrumented build in exchange for accuracy based on real compilation rather than text matching. That is the axis the project trades on. A tool that greps your source for `use` statements would run in milliseconds on stable, and it would be wrong about anything reached through macros, re-exports, proc macros or build scripts. The cost of cargo-udeps is that nightly is a moving target and the tool must be rebuilt against each one.

The other axis is scope. cargo-udeps looks at unused dependencies, not unused features and not pruning. Feature-level analysis and manifest pruning are separate concerns with separate tools, and nothing here claims otherwise.

Licensing is unusually permissive for this category: the tool is distributed under both MIT and Apache 2.0 at your option, and contributions are dual licensed the same way unless a contributor says otherwise explicitly. Both licences are permissive, so there is no copyleft obligation if you vendor it internally.

Editorial conclusion

cargo-udeps is the right tool when you suspect a `Cargo.toml` has accumulated dependencies nobody removed, and the wrong tool when you want a fast check on every commit. It needs `cargo +nightly udeps` rather than a stable toolchain, and it inspects a nightly-instrumented build, so it is slower than a metadata query. Two documented blind spots shape how you should read its output: crates used only by `std` and its dependencies, and crates already used by another dependency of the crate you are checking, will not be reported. Suppress what you know is legitimate through `package.metadata.cargo-udeps.ignore`, or at workspace level through `workspace.metadata.cargo-udeps.ignore`. The last push was on 2026-04-29 and v0.1.61 shipped the same day, so the tool is current, and `cargo install cargo-udeps --locked` gives you a binary rather than a git dependency you have to keep rebuilding.

Frequently asked questions

How do I remove unused dependencies in Cargo?

`cargo +nightly udeps` prints an unused crates line listing what it found, and you delete those entries from `Cargo.toml`. It needs Rust nightly to run, and it reports false positives in two documented cases: crates used only by `std` and its dependencies, and crates already used by another dependency of yours.

How do I stop cargo-udeps reporting a dependency I know is used?

Add the crate name to `package.metadata.cargo-udeps.ignore` in your `Cargo.toml`, under `normal`, `development` or `build` to match the dependency kind. In a workspace, use `workspace.metadata.cargo-udeps.ignore` in the root manifest to apply it to every member package.

Does cargo-udeps work on stable Rust?

No. The README states that compilation of the tool works on stable, but running it needs Rust nightly, which is why the documented usage is `cargo +nightly udeps`. If your CI standardises on a stable toolchain, that job needs a nightly toolchain installed.

Official sources

  1. est31/cargo-udeps on GitHub
  2. Issues
  3. README
  4. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/est31-cargo-udeps.svg)](https://hysenlabs.com/projects/est31-cargo-udeps)