Feature Flags in Rust: The Secret Weapon for Flexible Development
\n\nImagine you are writing a library for working with images. Some users only want basic resizing, others want advanced color correction, and still others want integration with neural networks. How do you make everyone happy without bloating the code or creating dozens of versions? The answer is simple — feature flags (or feature toggles).
\n\nIn the world of Rust, this technique becomes a true art. Thanks to a powerful macro system and conditional compilation, you can create modular, configurable applications that adapt to any scenario. And yes, you won't have to recompile the entire project for one small feature.
\n\nIn this article, we will break down how feature flags in Rust work, show their advantages with real examples, and give practical advice. Let's go!
\n\nWhat are feature flags and why are they needed?
\n\nFeature flags (or conditional compilation) is a mechanism that allows you to enable or disable certain parts of the code at build time. In Rust, this is implemented through the features system in Cargo.toml and the #[cfg(feature = "...")] directives.
Why is this needed? Here are three main scenarios:
\n\n- \n
- Library modularity — the user decides which functions they need. \n
- Experimental features — you can test new functionality without risk to production. \n
- Platform dependency — different code for Windows, Linux, and macOS. \n
Unlike runtime flags (which are checked during execution), Cargo features work at compile time. This means unused code doesn't even make it into the binary — you get maximum performance and minimal size.
\n\nHow feature flags work in Rust: basic syntax
\n\nLet's get straight to the code. Suppose we are writing a library image_tools with optional WebP support.
First, define the feature in Cargo.toml:
[package]\nname = "image_tools"\nversion = "0.1.0"\n\n[features]\ndefault = ["png"]\npng = []\nwebp = []\n\n\nNow in the code, use the #[cfg] attribute:
// src/lib.rs\n\n#[cfg(feature = "png")]\npub mod png_processor;\n\n#[cfg(feature = "webp")]\npub mod webp_processor;\n\npub mod common;\n\n// Inside the module:\n#[cfg(feature = "webp")]\npub fn encode_webp(data: &[u8]) -> Vec<u8> {\n // implementation for WebP\n println!("Encoding WebP...");\n data.to_vec()\n}\n\n#[cfg(not(feature = "webp"))]\npub fn encode_webp(data: &[u8]) -> Vec<u8> {\n panic!("WebP support not enabled. Enable 'webp' feature.");\n}\n\n\nNotice the clever trick: we provide a stub for the case when the feature is disabled. This allows you to keep the API unified, but when trying to use an unsupported function — get a clear error.
\n\nAdvanced techniques: combinations and dependencies
\n\nFeature flags in Rust allow you to create complex hierarchies. For example, you can make a "full" feature that includes several at once:
[features]\ndefault = ["basic"]\nbasic = ["resize", "crop"]\nadvanced = ["color_correction", "filters"]\nfull = ["basic", "advanced", "webp"]\n\n\nYou can also specify dependencies on external crates:
\n\n[features]\nwebp = ["dep:libwebp-sys"] # includes system dependency\n\n[dependencies]\nlibwebp-sys = { version = "0.1", optional = true }\n\n\nHere, optional = true tells Cargo that the crate is only included when the corresponding feature is activated. This saves build time and disk space.
Feature flags in a real project: HTTP server example
\n\nLet's imagine we are writing a mini-framework for