Features
Cargo features ([features] in Cargo.toml) are named conditional-compilation flags that allow downstream consumers to opt into or out of optional functionality, extra trait implementations, or heavy dependencies. When Cargo builds a crate with a feature enabled, it passes --cfg feature="<name>" to rustc.
Cargo.toml:
[features]
default = ["std"]
std = ["alloc"]
alloc = []
json = ["std"]
|
v `cargo build --no-default-features --features alloc`
Compiler receives:
`--cfg feature="alloc"`
Declaring and Using Features
[package]
name = "data_frame"
version = "0.1.0"
edition = "2024"
[features]
# Enabled automatically unless caller passes `--no-default-features`
default = ["std"]
# Enabling `std` automatically enables `alloc`
std = ["alloc"]
alloc = []
csv-export = ["std"]
Inside Rust source code, inspect active features with #[cfg(feature = "...")] or the boolean macro cfg!(feature = "..."):
pub struct Record {
pub id: u64,
pub label: &'static str,
}
impl Record {
pub fn format_summary(&self) -> String {
if cfg!(feature = "csv-export") {
format!("{},{}", self.id, self.label)
} else {
format!("Record #{}: {}", self.id, self.label)
}
}
}
#[cfg(feature = "csv-export")]
pub fn csv_header() -> &'static str {
"id,label"
}
fn main() {
let rec = Record {
id: 7,
label: "sensor_alpha",
};
println!("{}", rec.format_summary());
}
Record #7: sensor_alpha
Command-Line Flags for Selecting Features
| Command | Active Features |
|---|---|
cargo build |
Enables the default feature and everything listed in default = [...]. |
cargo build --features csv-export |
Enables default plus csv-export. |
cargo build --no-default-features |
Disables default (useful for no_std embedded builds). |
cargo build --no-default-features --features alloc |
Enables only alloc. |
cargo test --all-features |
Enables every feature defined in [features]. |
Feature Unification and Why Features Must Be Additive
When multiple crates in a dependency graph depend on the same shared crate foo with different feature sets (for example, Crate A enables foo/json and Crate B enables foo/tls), Cargo unifies their features and compiles foo once with the union of all requested features (json + tls).
[ Application ]
/ \
v v
[ Crate A ] [ Crate B ]
(foo/json) (foo/tls)
\ /
v v
[ Shared Crate `foo` ]
Compiled ONCE with `json` + `tls`!
Because of feature unification:
- Features must always be strictly additive: Enabling a feature may add new modules, functions, or trait implementations, but must never remove or change the signature of existing public items.
- Avoid mutually exclusive features (like
backend-opensslvsbackend-rustlsthat fail to compile when both are enabled at once), becausecargo test --all-featuresor feature unification across two dependencies will enable both simultaneously. If a fallback priority is needed when both are enabled, select one deterministically viaif cfg!(...) / else if cfg!(...).
Note that since Cargo Resolver v2 (standard in edition = "2021" and "2024"), features enabled only by [dev-dependencies] or [build-dependencies] are not unified into normal target builds unless those targets also request them.