Rust
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Ownership and Borrowing
-
Section 4: Structuring Data
-
Section 5: Collections and Error Handling
-
Section 6: Traits and Generics
-
Section 7: Concurrency
-
Section 8: Building for the Web
-
Section 9: Memory and Performance
-
Section 10: More Standard Library and Ecosystem
-
Section 11: Advanced Rust
-
Section 12: Rust for Systems and WebAssembly
-
Section 13: Tooling and Best Practices
-
Section 14: Data Structures and Algorithms in Rust
-
Section 15: Practical Projects
-
Section 16: Interview Practice
-
Section 17: std::collections In Depth
-
Section 18: std::io and std::fs In Depth
-
Section 19: std::net
-
Section 20: std::option and std::result In Depth
-
Section 21: std::iter In Depth
-
Section 22: std::sync In Depth
-
Section 23: std::string and std::str
-
Section 24: Cargo and Crates.io Ecosystem
-
Section 25: Popular Crates Ecosystem
-
Section 26: Rust Memory Model Deep Dive
-
Section 27: More Practice Exercises
-
Section 28: More Interview Practice
-
Section 29: Async Rust Deep Dive
-
Section 30: Tokio Ecosystem In Depth
-
Section 31: Error Handling Ecosystem Deep Dive
-
Section 32: Serde In Depth
-
Section 33: Web Development with Rust Deep Dive
-
Section 34: Database Access Ecosystem
-
Section 35: Rust for Embedded Systems Deep Dive
-
Section 36: Rust Macros In Depth
-
Section 37: Advanced Trait System
-
Section 38: Unsafe Rust In Depth
-
Section 39: Rust CLI Development
-
Section 40: Testing Ecosystem Deep Dive
-
Section 41: WebAssembly Deep Dive
-
Section 42: Rust Design Patterns
-
Section 43: More Data Structures in Rust
-
Section 44: Final Practice Projects
-
Section 45: Rust Performance Optimization
-
Section 46: Rust Ecosystem Tooling
-
Section 47: More Interview and Review
127: Feature Flags in Cargo
I once spent two hours debugging a "missing function" error in a project I had written myself. I knew the function was there—I could see it on my screen—but the compiler insisted it didn't exist. It turns out I had wrapped the function in a feature flag and forgotten to enable that flag in my binary's configuration. It's a rite of passage in Rust: the feeling that the compiler is gaslighting you.
When your functions suddenly vanish
Let's look at a scenario you'll likely run into. Imagine you're building a small library called telemetry. You want to give users the option to export data as JSON, but since serde_json is a heavy dependency, you don't want to force every user to download it.
// In telemetry/src/lib.rs
#[cfg(feature = "json")]
pub fn export_json(data: &str) -> String {
format!("{{\"data\": \"{}\"}}", data)
}
pub fn log_simple(data: &str) {
println!("Log: {}", data);
}
Everything looks great in the library. But then you try to use it in your main application:
// In app/src/main.rs
use telemetry;
fn main() {
telemetry::log_simple("System Start");
let json = telemetry::export_json("System Start"); // ERROR: no function `export_json` found in module `telemetry`
println!("{}", json);
}
If you're looking at the telemetry source code, you'll be confused. The function is right there! The problem is that #[cfg(feature = "json")] tells the compiler: "Only include this code if the json feature is active." By default, features are off unless you explicitly mark them as default. To the compiler, that function literally doesn't exist during the build process of your app.
Wiring the toggle in Cargo.toml
To fix this, you need to tell Cargo that your library has a feature called "json", and then tell your application to use it. First, in the library's Cargo.toml, you define the feature:
[features]
# This defines a feature called "json".
# It's empty for now because we're just using it as a toggle.
json = []
[dependencies]
# We'll get to the dependency part in a second
Now, in your application's Cargo.toml, you don't just list the library; you specify which features you want:
[dependencies]
telemetry = { version = "0.1", features = ["json"] }
Once you save this, the compiler sees the json flag is active, the #[cfg]` attribute evaluates to true, and the function is finally "baked into" the binary. I usually find it helpful to remember that features are additive. Adding a feature should never break existing functionality; it should only add new capabilities.
Tying features to optional dependencies
In the real world, you aren't just toggling a boolean; you're usually toggling a dependency. If your export_json function actually used the serde_json crate, you wouldn't want serde_json to be compiled if the user doesn't need JSON support.
The trick here is the optional = true key. Here is how I set up my Cargo.toml when I want a dependency to be tied to a feature:
[dependencies]
# Making it optional automatically creates a feature with the same name
serde_json = { version = "1.0", optional = true }
[features]
# This tells Cargo: "If the 'json' feature is enabled, also enable the 'serde_json' dependency"
json = ["dep:serde_json"]
Note the dep:serde_json syntax. In older versions of Rust, making a dependency optional automatically created a feature of the same name. Nowadays, using dep: allows you to name your feature (e.g., "json") differently than the crate name (e.g., "serde_json"), which keeps your API cleaner for the end user.
Now, when you wrap your code in #[cfg(feature = "json")], you are guaranteed that serde_json is available in the namespace. If you try to use serde_json outside of that cfg block, the compiler will throw an error—which is exactly what we want. It prevents you from accidentally leaking a heavy dependency into the "core" part of your library.
📋 Practical Task
Implementing a Conditional Compression Module
You are building a data-processing library. You want to implement a compress_data function, but it requires the flate2 crate. To keep the library lightweight, this should be an optional feature.
Requirements:
- Modify the
Cargo.tomlto includeflate2as an optional dependency. - Define a feature named
compressionthat enables theflate2dependency. - In
src/lib.rs, create a functioncompress_data(data: &str) -> Vec<u8>. - Ensure
compress_datais only compiled when thecompressionfeature is enabled. - Create a second function
process_data(data: &str)that callscompress_dataonly if the feature is enabled, and otherwise just returns the data as bytes. (Hint: Use#[cfg(feature = "compression")]and#[cfg(not(feature = "compression"))]).
There are no comments for now.