In this lesson, you will learn how to use Cargo features to keep your library lean by making non-essential functionality optional.
The Problem of Bloat
When building complex systems, especially those interfacing with external FFI boundaries or heavy dependencies, you often provide "batteries-included" features. However, a user might only need your core logic and not the optional heavy-duty features (like JSON serialization or specific platform-native drivers). Including these by default increases binary size and compile times for everyone.
Defining Features in Cargo.toml
To solve this, we define optional dependencies or flags in our Cargo.toml.
[features]
default = []
# An optional feature that brings in an external dependency
serde-support = ["dep:serde"]
[dependencies]
serde = { version = "1.0", optional = true }
Conditional Compilation in Code
Inside your library, you use the #[cfg(feature = "...")] attribute to conditionally compile code blocks. This allows the compiler to completely strip out the code—and its associated dependencies—if the feature is not enabled.
#[cfg(feature = "serde-support")]
use serde::{Serialize, Deserialize};
#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))]
pub struct SensorData {
pub value: f64,
}
pub fn process_data(data: &SensorData) {
// Core logic always present
println!("Processing: {}", data.value);
#[cfg(feature = "serde-support")]
{
// This block only exists if the feature is active
let json = serde_json::to_string(data).unwrap();
println!("Serialized: {}", json);
}
}
Architectural Impact
By gating optional functionality, you provide an "opt-in" experience. Users can toggle these features in their own Cargo.toml using features = ["serde-support"]. This technique is essential for library authors because it allows you to support a wide ecosystem of tools without forcing your users to pay the "dependency tax" for features they don't use. It effectively turns your library into a modular system where the build output is tailored specifically to the user's requirements.
Key Takeaway: Conditional feature flags allow you to keep your library modular and lightweight by excluding unnecessary code and dependencies from the final compilation unless explicitly requested by the user.