Feature Flags
Cargo features toggle optional dependencies and #[cfg(feature = "...")] code paths.
Recipe
[features]
default = ["std"]
std = []
json = ["dep:serde_json"]
[dependencies]
serde_json = { version = "1", optional = true }#[cfg(feature = "json")]
pub fn parse_json(s: &str) -> serde_json::Value {
serde_json::from_str(s).unwrap()
}When to reach for this: Optional integrations, no_std, compile-time trimming.
Working Example
#[cfg(feature = "async")]
pub mod async_api;
#[cfg(not(feature = "async"))]
pub mod sync_api;Compile only one API surface per feature set.
What this demonstrates:
cfgattribute gates modules- Optional deps tied to features via
dep:name(Cargo 1.60+) - Default features document common build
Deep Dive
--no-default-features, --all-features for CI matrix. Feature unification in workspaces.
Gotchas
- Breaking feature rename - Semver. Fix: Deprecation period.
- feature = dep without optional - Manifest error. Fix:
optional = true. - cfg in public API - Docs differ per feature. Fix:
cargo doc --all-features. - Implicit features removed - Use explicit feature = ["dep:serde"].
- Mutually exclusive features - Document; compile both may error.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Separate crates | Large optional stacks | Small flag |
| Runtime config | User toggles at run | Compile-time trim needed |
cfg(target_os) | Platform code | Optional user feature |
FAQs
weak features?
dependency weak feature forwarding.required-features bin?
[[bin]] required-features.test features?
dev-dependencies separate.docs.rs all-features?
[package.metadata.docs.rs].no default?
default = [] minimal.feature combinatorics?
Test common combos CI.cfg macros?
cfg_if crate.rustflags cfg?
RUSTFLAGS --cfg custom.wasm features?
Smaller default features.clippy cfg?
cfg attributes respected.Related
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.