Feature-Based Compilation
Feature-based compilation uses Cargo's [features] table in Cargo.toml together with #[cfg(feature = "...")] attributes in Rust source code to make optional dependencies, heavy subsystems, and serialization integrations opt-in at compile time.
Defining Features in Cargo.toml
In Cargo.toml, features are declared under [features]. The special feature named default lists the features enabled automatically unless a downstream user sets default-features = false:
[package]
name = "http-client"
version = "0.1.0"
edition = "2024"
[dependencies]
# Optional dependencies are not compiled unless activated by a feature:
serde = { version = "1.0", optional = true, features = ["derive"] }
flate2 = { version = "1.0", optional = true }
[features]
# Enabled by default:
default = ["compression"]
# Activates the optional `flate2` dependency using `dep:` syntax:
compression = ["dep:flate2"]
# Activates optional `serde` dependency:
json = ["dep:serde"]
# Meta-feature enabling all capabilities:
full = ["compression", "json"]
The Fundamental Rule: Features Must Be Strictly Additive!
When multiple crates in a dependency tree depend on your library with different feature sets (crate_a enables feature = "json", while crate_b enables feature = "compression"), Cargo unions all requested features into a single compiled copy of your library (--cfg feature="json" --cfg feature="compression").
Cargo Feature Unification Across a Dependency Graph:
[ Binary App ]
/ \
v v
[crate_a] [crate_b]
(wants "json") (wants "compression")
\ /
v v
[http-client] Compiled ONCE with BOTH `feature = "json"` AND `feature = "compression"`!
Because Cargo unions features across the entire dependency graph:
- Enabling a feature must only ADD items, trait implementations, or capabilities—never remove or change the signature of existing public items!
- Avoid mutually exclusive features (such as
backend-opensslvs.backend-rustlswhere enabling both fails to compile). If two features control a runtime default, define a clear precedence order when both are enabled:
pub struct Payload {
pub raw: Vec<u8>,
}
impl Payload {
pub fn new(raw: Vec<u8>) -> Self {
Self { raw }
}
// Always available regardless of feature flags:
pub fn len(&self) -> usize {
self.raw.len()
}
// Strictly additive method unlocked when `feature = "hex"` is enabled:
#[cfg(feature = "hex")]
pub fn to_hex(&self) -> String {
self.raw.iter().map(|b| format!("{:02x}", b)).collect()
}
}
fn main() {
let p = Payload::new(vec![0xDE, 0xAD, 0xBE, 0xEF]);
println!("payload len = {} bytes", p.len());
}
payload len = 4 bytes
Cargo CLI Commands for Testing Feature Combinations
| Command | Effect |
|---|---|
cargo test |
Compiles and tests with default features |
cargo test --no-default-features |
Verifies the crate compiles when all default features are disabled |
cargo test --all-features |
Verifies the crate compiles when every feature is enabled simultaneously |
cargo test --features "json,compression" |
Enables specific features on top of default |