cargo-hack runs your Rust test suite once per feature combination
Cargo subcommand to provide various options useful for testing and continuous integration.
At a glance
- What is it?
- cargo-hack is a Cargo subcommand that turns a single test run into a matrix, one run per feature or one per combination in the powerset. The useful flags are well thought through, but the dev-dependency options edit your real Cargo.toml while the tool runs, and the visible documentation does not spell out the installation commands.
- Who is it for?
- cargo-hack is worth adopting for any Rust crate with more than a couple of optional features, especially one that publishes to crates.io and cannot assume the default feature set is the only combination that matters. It is not the right tool for a workspace where features are decorative or where CI time is dominated by something other than compilation.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 13 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 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One cargo test run becomes a matrix of builds
The problem cargo-hack solves is not speed, it is coverage of the feature space. A Rust crate with optional dependencies behind features can compile and test cleanly with the default features on, then fail to build the moment a user turns one of them on. Finding that by hand means writing out each feature name and running the test command repeatedly. The tool folds that loop into a single invocation, and the option list in the documentation is organised around exactly that idea.
The command itself is a Cargo subcommand, so it is invoked as cargo hack and inherits Cargo's own package selection flags.
$ cargo hack --help
cargo-hack
Cargo subcommand to provide various options useful for testing and continuous integration.
USAGE:
cargo hack [OPTIONS] [SUBCOMMAND]
Use -h for short descriptions and --help for more details.Because it sits behind the cargo entry point, the package, workspace, exclude, manifest-path and locked options all behave the way a Cargo user expects. The tool can run over a whole workspace with --all, which is an alias for --workspace, or narrow down with --exclude. There is also an --ignore-private flag, which skips packages marked publish = false rather than attempting to check them.
--each-feature and --feature-powerset draw different lines
The two headline flags differ in how much they cover. --each-feature performs a run for each feature of the package, and its documentation states that it also includes runs with just --no-default-features and with the default features.
--each-feature
Perform for each feature of the package.
This also includes runs with just --no-default-features flag, and default features.--feature-powerset performs a run for each combination in the powerset, with the same additions of the no-default-features run and the default run. The documentation also notes that when the powerset flag is used with --depth or with namespaced features, and not narrowed by --exclude-features or --include-features, the run with --all-features is included as well.
This is the practical difference: --each-feature gives you single features plus the two ends of the range, while --feature-powerset gives you every combination, which grows quickly with the number of features. Both also accept --include-features to restrict the combinations to a chosen list, and both refuse to work without one of the two flags present, so the flags cannot be combined with each other by accident. That guard is the sort of detail that saves a confusing CI failure.
--depth, --group-features and the exclusion flags keep the matrix sane
A powerset is only useful if you can control its size, and the tool offers several ways to do that. --depth sets a maximum number of simultaneous feature flags, and the documentation states that a depth of 1 makes --feature-powerset equivalent to --each-feature. --group-features treats the listed features as if they were one, and the option can be repeated to define several groups.
There is also a set of constraints on what may appear together. --mutually-exclusive-features names features that should not be used together, and --at-least-one-of skips combinations of features that enable none of the listed ones. Both can be given more than once to describe several groups.
--group-features <FEATURES>...
Space or comma separated list of features to group.
This treats the specified features as if it were a single feature.On the exclusion side, --exclude-features (with --skip as an alias) takes feature names, and the documentation gives two special cases worth remembering. To exclude the run of the default feature you pass the value default, and separate flags --exclude-no-default-features and --exclude-all-features drop those specific runs. The exclusion and inclusion flags can only be used together with --each-feature or --feature-powerset, which keeps the semantics of a plain cargo run untouched.
--no-dev-deps rewrites your real Cargo.toml while it runs
This is the flag with a side effect worth understanding before you put it in a script. The documentation for --no-dev-deps says it performs without dev-dependencies, and that the flag removes dev-dependencies from the real Cargo.toml while cargo-hack is running and restores it when finished.
--no-dev-deps
Perform without dev-dependencies.
Note that this flag removes dev-dependencies from real `Cargo.toml` while cargo-hack is
running and restores it when finished.--remove-dev-deps is the same behaviour with the restoration step removed, so it leaves the manifest without dev-dependencies when it finishes. The intent is clear: a check that should work without dev-dependencies must not be able to rely on them, and some optional dependency combinations can only be validated that way.
The trade-off is that the manifest on disk is in a modified state while the command runs. If the process is interrupted, or if two cargo-hack runs overlap in the same checkout, the file being edited is the one your other tooling reads. It also means the tool needs write access to the manifest in every environment where it runs, including read-only CI checkouts.
Optional dependencies as features, and unknown features that are skipped
Cargo optional dependencies are features in disguise, and the tool exposes them directly. The --optional-deps flag treats optional dependencies as features, and if no dependency names are given, all optional dependencies are considered. Like the other feature-shaping options, it only works together with --each-feature or --feature-powerset.
The opposite case is handled by --ignore-unknown-features, which skips passing the --features flag to Cargo if that feature does not exist in the package. That is the escape hatch for a workspace where a shared runner passes a fixed list of feature names to several crates, some of which do not define every name.
There is a separate lever for crates that are never published. --no-private performs without publish = false crates, and --ignore-private skips those packages entirely rather than running a check on them. The distinction matters in a monorepo that mixes publishable libraries with internal applications, because the internal ones frequently carry features that were never meant to be checked in isolation.
The install path is documented, but not in the excerpt most readers see
The documentation for the project has an Installation section and a From source section, and the manifest keeps the two in sync: the rust-version field in Cargo.toml is 1.85, with a comment noting that it must match the minimum supported Rust version stated in the From source section of the README. The visible portion of the documentation does not spell out the commands, so the practical advice for a reader is to follow the Installation section of the project page directly rather than reconstruct it from guesswork.
The manifest itself is a good description of what the tool depends on. The runtime dependencies are anyhow for error handling, cargo-config2 for reading Cargo configuration, ctrlc for signal handling, lexopt for argument parsing, same-file, serde_json, termcolor for coloured output, and toml_edit for editing manifests, which is what makes the dev-dependency removal possible without reformatting the file. The dev-dependencies pull in tempfile, easy-ext, build-context, and a pinned git revision of a test helper.
The package is published under the cargo-plugins and testing categories, and the manifest excludes dotfiles and the tools directory from the published archive, so the source tree you clone is slightly larger than the one you install.
Release cadence versus the current main branch
The published versions tell a slow story. The listed releases are v0.6.45 on 2026-05-30, v0.6.44 on 2026-03-20 and v0.6.43 on 2026-02-13, and the manifest version field matches 0.6.45. The last push to the repository was on 2026-10-10, which is several months after the most recent tag, so work is landing on main that has not been cut into a release yet.
For a tool that people put in CI, that gap is the thing to watch. If you pin a version, you pin to a matrix behaviour that was verified at tag time. If you track main, you pick up fixes but you also accept that the option surface can change, since this is a pre-1.0 tool where flags are still being adjusted.
The licence is Apache-2.0 OR MIT, dual licensed, so it fits into projects with either requirement. It is a command line tool that runs inside your own toolchain and does not phone home or require a service, which is what makes it reasonable to run against an internal registry.
Editorial conclusion
cargo-hack is worth adopting for any Rust crate with more than a couple of optional features, especially one that publishes to crates.io and cannot assume the default feature set is the only combination that matters. It is not the right tool for a workspace where features are decorative or where CI time is dominated by something other than compilation. Before wiring it into a pipeline, check the exact feature names in your own Cargo.toml, run one --each-feature pass locally to see how long the matrix takes, and decide deliberately whether the --no-dev-deps rewrite of your manifest is acceptable on developer machines.
Frequently asked questions
What does cargo hack --each-feature actually run?
It performs a run for each feature of the package, and the documentation states that this also includes runs with just --no-default-features and with the default features. It can only be used together with --feature-powerset or with the feature-shaping options such as --exclude-features and --include-features.
How do I stop cargo-hack from testing a particular feature?
Use --exclude-features, or its alias --skip, with the feature names. To drop the default feature run you pass the value default, and the separate flags --exclude-no-default-features and --exclude-all-features remove those specific runs.
Does cargo-hack modify my Cargo.toml?
Yes, when you use --no-dev-deps it removes dev-dependencies from the real Cargo.toml while the tool runs and restores the file when finished. The variant --remove-dev-deps does the same thing without restoring the original file.
How large does the feature powerset matrix get?
The --depth option sets a maximum number of simultaneous feature flags, and the documentation notes that --depth 1 makes --feature-powerset equivalent to --each-feature. You can also collapse features with --group-features and skip invalid combinations with --mutually-exclusive-features and --at-least-one-of.
What minimum Rust version does cargo-hack need?
The manifest sets rust-version = 1.85, with a comment that this must stay in sync with the minimum supported Rust version given in the From source section of the README.
Official sources
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.
[](https://hysenlabs.com/projects/taiki-e-cargo-hack)Was this useful?