Conditional Compilation & Attributes (cfg
& cfg_attr
)
Rustβs cfg
system allows conditional compilation based on platform, features, or custom flags. It works at compile time, so irrelevant code is never even built.
Usage Forms
Attributes
#[cfg(target_os = "linux")]
fn special_linux_function() { /* ... */ }
#[cfg(feature = "serde")]
mod with_serde;
Inline Expressions (cfg! macro
)
cfg!
expands to abool
at runtime (evaluated at compile-time, but value is inlined).
Conditional Attributes (cfg_attr
)
- If this
cfg condition is true
,apply this other attribute
. If not, ignore it.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
struct Point {
x: i32,
y: i32,
}
Common Predicates
target_os = "linux" | "windows" | "macos" | ...
target_arch = "x86" | "x86_64" | "arm" | ...
target_pointer_width = "32" | "64"
debug_assertions
βtrue
in debug buildsfeature = "name"
β enabled Cargo feature (specify inCargo.toml
)
What is debug_assertions
?
This is enabled by default in debug builds and disabled in release builds. It allows you to include code that should only run in debug mode, such as additional checks or logging.
fn main(){
if cfg!(debug_assertions){
println!("debug mode");
} else {
println!("release mode");
}
}
- run in debug & release mode:
Compound Conditions
#[cfg(all(unix, feature = "logging"))]
fn log_unix() {}
#[cfg(any(target_os = "linux", target_os = "macos"))]
fn posix_only() {}
#[cfg(not(feature = "gui"))]
fn headless() {}
Cargo Integration
Features are defined in Cargo.toml
:
- A feature is a mapping:
name = [ list of other features or optional deps ]
. default
(special) β automatically enabled unless--no-default-features
.- Entries in the list can be:
- another feature name (enables that feature),
dep:NAME
to pull in an optional dependency.
Example
[dependencies]
dep1 = { version = "1", optional = true }
dep2 = { version = "1", optional = true }
dep3 = { version = "1", optional = true }
[features]
default = []
feat1 = ["dep:dep1", "dep:dep2/featureA"]
feat2 = ["feat1", "dep:dep3"]
- All features are disabled by default. To make them enabled by default, add them to
default
:
cargo run --features feat2
β enablesfeat2
, which pulls infeat1
,dep1
,dep2
, anddep3
.- Optional deps (
optional = true
) are not included unless gated behind a feature that enables them. - To enable specific feature of dependency:
dep_name/feature_name
. - Use
?/
to make a feature optional:dep_name?/feature_name
. It means, "ifdep_name
is already included (by default or some other feature), then only enablefeature_name
".
see example from tokio project
[package]
name = "tokio-stream"
# When releasing to crates.io:
# - Remove path dependencies
# - Update CHANGELOG.md.
# - Create "tokio-stream-0.1.x" git tag.
version = "0.1.17"
edition = "2021"
rust-version = "1.70"
authors = ["Tokio Contributors <team@tokio.rs>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
description = """
Utilities to work with `Stream` and `tokio`.
"""
categories = ["asynchronous"]
[features]
default = ["time"]
full = [
"time",
"net",
"io-util",
"fs",
"sync",
"signal"
]
time = ["tokio/time"]
net = ["tokio/net"]
io-util = ["tokio/io-util"]
fs = ["tokio/fs"]
sync = ["tokio/sync", "tokio-util"]
signal = ["tokio/signal"]
[dependencies]
futures-core = { version = "0.3.0" }
pin-project-lite = "0.2.11"
tokio = { version = "1.15.0", path = "../tokio", features = ["sync"] }
tokio-util = { version = "0.7.0", path = "../tokio-util", optional = true }
[dev-dependencies]
tokio = { version = "1.2.0", path = "../tokio", features = ["full", "test-util"] }
async-stream = "0.3"
parking_lot = "0.12.0"
tokio-test = { version = "0.4", path = "../tokio-test" }
futures = { version = "0.3", default-features = false }
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
# Issue #3770
#
# This should allow `docsrs` to be read across projects, so that `tokio-stream`
# can pick up stubbed types exported by `tokio`.
rustc-args = ["--cfg", "docsrs"]
[lints]
workspace = true
tokio/sync
feature is included by default, bcoz nooptional = true
. But, other features liketokio/fs
is enabled only when using--features fs
.
Using:
- Enable with:
cargo build --features serde
cargo build --features "serde extra"
cargo test --no-default-features --features serde
Dependencies per feature
[dependencies]
serde = { version = "1", optional = true }
regex = { version = "1" }
[features]
default = ["regex"]
serde_support = ["serde"]
optional = true
β only included if its feature is enabled.
Tests with cfg
#[cfg(test)]
mod tests {
#[test]
fn basic() {}
}
#[cfg(all(test, feature = "serde"))]
mod serde_tests {
#[test]
fn test_with_serde() {}
}
Notes
#[cfg]
removes code entirely at compile-time.cfg!
only provides abool
; it does not remove code.cfg_attr
allows for conditional attributes.- Use attributes for conditional modules, imports, or functions.
- Use
cfg!
for branching in code paths.