In Cargo, an optional dependency is only built when a feature enables it. A manifest can list a crate that your final binary never contains, or contain one only under a feature flag. To judge exposure, check which features your build turns on, not just what Cargo.toml mentions.
How features and optional dependencies work
The Cargo Book describes features as a mechanism for conditional compilation and optional dependencies. A package defines named features in the [features] table of Cargo.toml, and each is either enabled or disabled. Features for the package being built can be enabled on the command line with flags such as --features. Features of a dependency can be enabled in the dependency declaration.
Each feature specifies an array of other features or optional dependencies that it enables. By default, an optional dependency creates an implicit feature of the same name. If you reference the optional dependency with the dep: prefix anywhere in the [features] table, that disables the implicit feature. The docs note the dep: syntax is available from Rust 1.60.
Their example: a library supporting the AVIF image format declares two optional dependencies, ravif and rgb, and an avif feature that lists "dep:ravif" and "dep:rgb". Enabling avif brings in both. The implicit features named ravif and rgb are not created, so users cannot switch them on individually.
Defaults matter
The docs also cover default features. If you depend on a crate without setting default-features to false, its default features are enabled, which can pull in optional dependencies you did not choose. The "package-name/feature-name" syntax enables a feature of a dependency, and it also enables the dependency itself if it is optional, unless you use the question mark form ("package-name?/feature-name"), which only enables the feature if something else enabled the dependency.
| Declaration | Effect |
|---|---|
| optional = true on a dependency | Built only when a feature enables it |
| A feature listing "dep:crate" | Enables the optional dependency, no implicit feature |
| default-features = false | Skips the dependency's default features |
| "crate?/feature" | Enables a feature only if the crate is already enabled |
What this means for security checks
A scan of the manifest lists optional dependencies whether or not they are built. That can overstate risk: an advisory for an optional crate may not apply if no enabled feature reaches it. It can also hide risk, if a downstream user enables a feature you never tested.
Two questions give you a reliable answer:
- Which features does the final application enable, including through its dependencies' defaults?
- For an advisory on a crate, is that crate in the resolved build for those features?
Confirm with Cargo's own tooling for your project, such as its dependency tree command with the same feature flags you use to build, rather than reasoning from the manifest alone.
For library authors
If you publish a library, document which features pull in which optional crates, and keep default features small. Fewer defaults means fewer surprise dependencies for your users, and each one you avoid is one nobody has to track advisories for.
Steps to review a crate
- List optional dependencies in Cargo.toml.
- Map each to the features that enable it.
- Check your release build's feature set.
- Compare the resulting tree with what your scanner reports.
- Reduce default features where you can.
Safeguard offers reachability analysis alongside SBOM and dependency scanning, which helps separate dependencies that are present from code that is actually used. The free scan is a way to see an inventory for a repository of your own.
Quick check
Does an optional dependency always end up in the binary?
No. It is built only when an enabled feature turns it on.
What does the dep: prefix do?
It enables an optional dependency from a feature and disables the implicit feature of the same name. It needs Rust 1.60 or later.
Sources
Last reviewed: 19 October 2026.