We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Modules, Visibility, Testing and Docs step 16 of 22
Cargo features and the additivity rule
Enabling a Cargo feature can enable other features and optional dependencies. Those can enable more features in turn. Implement a resolver that follows these links until nothing new becomes active:
pub fn resolve_features(
features: Vec<(String, Vec<String>)>,
optional_deps: Vec<String>,
requested: Vec<String>,
no_default: bool,
) -> Vec<String>
This is a simplified model of one package's feature table. It does not read
Cargo manifests or resolve dependency versions, targets or dependency feature
tables. Return a sorted, duplicate-free Vec<String> of activation markers.
Build the feature table
features contains (name, entries) pairs. If a name appears more than once,
use its last entry list. optional_deps names optional dependencies.
Before resolving requests, add an implicit feature for each optional dependency
that is never mentioned as dep:name anywhere in the table. The implicit
feature is named name and contains the single entry dep:name. Keep an
explicitly defined feature of that name instead of replacing it.
For example, an optional dependency named log normally adds the feature
log = ["dep:log"]. A dep:log entry in any explicit feature suppresses that
automatic addition, even if that explicit feature is never activated.
Start with the requested names that exist in the resulting table. Also enable
default if it exists and no_default is false. Setting no_default only
skips that automatic request: default may still be requested explicitly or
enabled by another feature. Ignore unknown requested names.
Expand active features
Each entry has one of four forms:
| Entry | Effect | Output marker |
|---|---|---|
name |
Enable this package's named feature if it exists | name |
dep:pkg |
Enable the optional dependency | dep:pkg |
pkg/feat |
Enable the dependency feature; also enable pkg if optional |
pkg/feat, plus dep:pkg if optional |
pkg?/feat |
Enable the dependency feature only if dep:pkg becomes active |
pkg/feat when enabled |
The weak form, pkg?/feat, never enables the dependency itself. Inputs use it
only with optional dependencies, and dep:pkg also names an optional
dependency. Other slash entries may refer to non-optional dependencies, which
are treated as already available and do not get a dep: marker.
Names and entries are well-formed and contain no surrounding whitespace.
Plain feature names do not contain : or /. Unknown plain names in entry
lists are ignored. Dependency feature markers are terminal: record them, but
do not try to expand another package's feature table.
Revisit weak entries when dependencies become active
Suppose serde is optional and the feature table contains:
[features]
default = ["json"]
json = ["dep:serde", "serde?/derive"]
extra = ["serde?/rc"]
With automatic defaults disabled, the results are:
| Requested features | Sorted result |
|---|---|
[] |
[] |
["extra"] |
["extra"] |
["extra", "json"] |
["dep:serde", "extra", "json", "serde/derive", "serde/rc"] |
["default"] |
["default", "dep:serde", "json", "serde/derive"] |
extra itself is active even when its weak entry has no effect. If you visit
extra before json, you cannot discard serde?/rc permanently: json will
later activate serde.
One approach is to expand every active feature repeatedly until the set stops
growing. This stable set is called a fixed point. Ordinary feature chains
also need to be followed beyond their first link. Cycles such as a -> b -> a
must terminate once both names have been seen. Duplicate requests and repeated
entries must not duplicate the output.
The starter is missing implicit features and performs only one sweep. Fix both parts. A work queue is another valid approach if it revisits weak entries when their dependencies become active.
Why Cargo features should be additive
Real Cargo combines feature requests for a dependency within a resolution context. One caller cannot subtract a feature enabled by another. Design features to add compatible functionality so combined requests still work.
For optional standard-library support, a common design is a std feature
that adds std-dependent APIs. It may be enabled by default. A caller using
default-features = false stops requesting those defaults on its own
dependency edge; another caller can still enable them. A no_std feature
that removes APIs others need makes such combinations harder to support.
This is not an unconditional union across an entire workspace. Resolver 2 separates normal dependencies from build dependencies and proc macros, ignores features for inactive target-specific dependencies, and includes dev-dependency features when building targets that need them. Resolver 3 retains these distinctions and changes how incompatible Rust versions are handled. Package versions and selected build targets also matter.
Implicit feature names can become part of a crate's public API. Removing one
can break users who request it. Using dep:name behind an explicit feature
lets you choose the feature name you expose; changing a published feature
still needs compatibility consideration.
Finally, this exercise intentionally ignores unknown feature names. Cargo validates real manifests and requests and can reject them instead. The exercise's duplicate-entry and explicit-feature rules likewise define this function's input behavior, not a complete Cargo manifest validator.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.