Shaping the feature matrix#
By default the matrix is the powerset of a package’s features. These keys, all under [package.metadata.cargo-fc], change what gets generated.
Configuration is validated strictly: naming a feature the package does not
declare — in any key, any scope (including target.'cfg(...)' sections that
don’t match the current host), and any patch operation — fails before anything
runs. A typo can therefore never silently shrink or grow the matrix, and stale
entries surface immediately when features are renamed or removed. This matches
Cargo’s own strictness for --features, which rejects even default when the
package declares no such feature.
At a glance#
| Key | Effect |
|---|---|
exclude_features | Remove features from the varied set. |
only_features | Restrict the varied set to an allowlist. |
include_features | Add features to every generated combination. |
mutually_exclusive_features | Permit at most one feature from each group. |
exclude_feature_sets | Drop specific combinations (e.g. incompatible pairs). |
include_feature_sets | Always add specific exact combinations. |
allow_feature_sets | Replace the powerset with an exact list of sets. |
isolated_feature_sets | Build independent sub-matrices and merge them. |
skip_optional_dependencies | Ignore implicit optional-dependency features. |
no_empty_feature_set | Never include the empty (no-features) combination. |
max_combinations | Raise the safety limit on generated combinations. |
matrix | Attach custom metadata to cargo fc matrix rows. |
exclude_features#
Features listed here are not varied in the matrix.
exclude_features = ["full"]An umbrella such as full may be redundant when its component features already vary independently.
Do not exclude default when the matrix must cover ordinary Cargo defaults: cargo-fc disables
defaults for every invocation, so [] enables no features and ["default"] enables the declared
default feature set.
only_features#
Restrict the combinatorial matrix to an allowlist. When set, features not listed are ignored. When empty, all features are considered.
only_features = ["core", "cli"]include_features#
Add features to every generated combination. This does not restrict which features are varied — it pins features that must always be on. To restrict the varied set, use only_features.
include_features = ["feature-that-must-always-be-set"]mutually_exclusive_features#
Declare groups of alternatives where each generated combination may contain at most one member. The no-member choice is included, and features outside the groups keep their full powerset:
mutually_exclusive_features = [
["cuda", "coreml", "webgpu"],
]With two other features, this produces 2² × (3 + 1) = 16 base combinations:
each independent-feature subset crossed with no backend, CUDA, Core ML, or
WebGPU. A newly added feature remains visible automatically; a newly added
backend also varies freely until it is deliberately added to the group.
Multiple groups are allowed but must be disjoint. include_feature_sets can
still add an exact combination containing
multiple group members, while a non-empty allow_feature_sets remains the
complete matrix and ignores the groups.
If include_features pins a group member, that member becomes the only group
choice — pinned features are added to every combination, so this holds even
when exclude_features or only_features removes the member from the varied
universe. Pinning two members of the same group is an error. Universe filters
can remove members from the varied choices but never add them.
Like the feature-set keys, this setting is patchable per target and command. Patch operations add or remove whole groups:
[package.metadata.cargo-fc.target.'cfg(target_os = "linux")']
mutually_exclusive_features = { add = [["openssl", "rustls"]] }exclude_feature_sets#
Drop groupings of features that are incompatible or don’t make sense together. Any generated combination that is a superset of a listed set is removed.
# `native-tls` and `rustls` are two TLS backends — enabling both makes no sense.
exclude_feature_sets = [["native-tls", "rustls"]]To exclude only the empty feature set, list it explicitly (or use no_empty_feature_set):
exclude_feature_sets = [[]]include_feature_sets#
Always add these exact combinations to the final matrix, unless one is already present. Other configuration is not applied to these sets.
# The exact stack you ship — always kept in the matrix, even if other rules
# (or pruning) would otherwise drop it.
include_feature_sets = [
["postgres", "rustls", "runtime-tokio"],
]allow_feature_sets#
When non-empty, the matrix becomes exactly the listed sets — no powerset is generated.
allow_feature_sets = [
["hydrate"],
["ssr"],
]This is the most direct way to say “only ever test these specific configurations.”
isolated_feature_sets#
For a crate whose features fall into independent groups that never interact. Instead of one powerset over all features, cargo fc builds a sub-matrix per group and merges them (a combination appearing in more than one group is kept once).
For example, a serialization crate that supports several formats and, independently, several compression codecs. The two axes are orthogonal — the format code doesn’t care which codec is enabled — so cross-testing every format subset against every codec subset adds no coverage:
isolated_feature_sets = [
["json", "yaml", "msgpack"], # formats
["gzip", "zstd", "brotli"], # compression codecs
]The full powerset here is 2⁶ = 64 combinations; the isolated sets reduce it to 2³ + 2³ − 1 = 15 (the shared empty set is merged). Other configuration options are still respected. See Large feature sets.
skip_optional_dependencies#
Cargo generates an implicit feature for each optional dependency (e.g. an optional serde dependency yields an implicit serde = ["dep:serde"] feature). Enabling this removes those implicit features from the matrix, mirroring the flag of the same name in cargo-all-features.
skip_optional_dependencies = trueUseful when optional dependencies would otherwise blow up the matrix. See Optional dependencies.
no_empty_feature_set#
Never include the empty combination (no --features), even if it would otherwise be generated.
no_empty_feature_set = truemax_combinations#
cargo fc fails if it would generate more than a safety limit (default 100000) of combinations. Raise it when you legitimately need more:
max_combinations = 250000Mutually exclusive groups are counted after applying their constraint. For
u unconstrained features and groups of effective sizes n₁, n₂, …, the
global base count is 2ᵘ × (n₁ + 1) × (n₂ + 1) × ….
matrix#
Attach custom metadata to every row a package emits in cargo fc matrix. See The matrix subcommand.
matrix = { kind = "ci" }[package.metadata.cargo-fc.matrix]
requires-gpu = falseAutomatic pruning#
Some features imply others. When a full feature enables both json and yaml, a combination like {full, json} resolves — after Cargo’s feature unification — to the same effective set as {full} alone. cargo fc drops the redundant combination automatically, so it isn’t checked twice.
[package]
name = "app"
version.workspace = true
edition.workspace = true
publish.workspace = true
[features]
# `full` implies both `json` and `yaml`, so combinations like `[full, json]`
# resolve to the same effective feature set as `[full]` alone and get pruned.
json = []
yaml = []
full = ["json", "yaml"]Pruning is on by default and needs no configuration — whatever your features, the smaller equivalent combination is kept and the redundant supersets are dropped. --show-pruned reveals what was dropped and why (the SKIP rows are each equivalent to [full]):
$ cargo fc --summary-only --show-pruned check --workspace Checking [1/5] app ( features = [] ) Checking [2/5] app ( features = [full] ) Checking [3/5] app ( features = [json] ) Checking [4/5] app ( features = [json, yaml] ) Checking [5/5] app ( features = [yaml] ) Finished 5 of 8 feature combinations for 1 package in 0.00s (3 pruned) PASS app ( 0 errors, 0 warnings, features = [] ) PASS app ( 0 errors, 0 warnings, features = [full] ) SKIP app ( 0 errors, 0 warnings, features = [full, json] ) ← equivalent to [full] SKIP app ( 0 errors, 0 warnings, features = [full, json, yaml] ) ← equivalent to [full] SKIP app ( 0 errors, 0 warnings, features = [full, yaml] ) ← equivalent to [full] PASS app ( 0 errors, 0 warnings, features = [json] ) PASS app ( 0 errors, 0 warnings, features = [json, yaml] ) PASS app ( 0 errors, 0 warnings, features = [yaml] )
You should not need to change this, but you can turn it off to check every generated combination regardless of unification — with --prune-implied=false for one run, or in config:
[workspace.metadata.cargo-fc]
prune_implied = falseSee also --show-pruned and --prune-implied.
All of these keys can be refined per target and per command using the same patch syntax. That’s the override model.