⚙️

Conditional Compilation

Build-Time Configuration and Feature Flags

🔧 Conditional Compilation Quick Reference

cfg()
Check compile-time configuration
#[cfg(target_os = "linux")] - Attribute form
if cfg!(debug_assertions) - Macro form
Feature Flags
Enable/disable optional code
#[cfg(feature = "json")] - Code inclusion
features = ["default"] - Cargo.toml
Boolean Logic
Complex conditions
all() - AND logic
any() - OR logic
not() - NOT logic
Custom cfg
User-defined configuration
--cfg custom_flag - Command line
println!("cargo:rustc-cfg=flag") - build.rs

cfg Attribute Grammar

cfg ( PREDICATE )
PREDICATE
KEY = "VALUE" | BOOLEAN_OP
BOOLEAN_OP
all(...) | any(...) | not(...)

Conditional Compilation Grammar

CFG_ATTR := "#[cfg(" PREDICATE ")]" PREDICATE := KEY "=" STRING | BOOLEAN_OP BOOLEAN_OP := "all(" PRED_LIST ")" | "any(" PRED_LIST ")" | "not(" PREDICATE ")" PRED_LIST := PREDICATE ("," PREDICATE)* KEY := "target_os" | "feature" | "debug_assertions" | CUSTOM

Actual Rust Usage

// Basic usage
#[cfg(target_os = "linux")]
fn linux_code() { /* ... */ }

// Boolean logic
#[cfg(all(unix, feature = "advanced"))]
fn unix_advanced() { /* ... */ }

// Runtime check
if cfg!(debug_assertions) {
    println!("Debug build");
}

🎮 Conditional Compilation Explorer

Conditional compilation in Rust allows you to include or exclude code based on compile-time conditions. This powerful feature enables cross-platform development, feature toggling, and build optimization through the cfg attribute system.

Using conditional compilation, you can write code that adapts to different target platforms, optional features, and build configurations while maintaining a single codebase.

The cfg Attribute

The cfg attribute is the primary mechanism for conditional compilation in Rust. It allows you to conditionally compile items based on configuration options.

// Conditional compilation based on target OS
#[cfg(target_os = "windows")]
fn get_config_dir() -> PathBuf {
    PathBuf::from(r"C:\ProgramData\MyApp")
}

#[cfg(target_os = "macos")]
fn get_config_dir() -> PathBuf {
    PathBuf::from("/Library/Application Support/MyApp")
}

#[cfg(target_os = "linux")]
fn get_config_dir() -> PathBuf {
    let home = env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
    PathBuf::from(format!("{}/.config/myapp", home))
}

// Usage remains the same across platforms
fn main() {
    let config_dir = get_config_dir();
    println!("Config directory: {:?}", config_dir);
}

Platform-Specific Code Generation

Pattern #[cfg(target_os = "{OS}")] fn {name}() Platform-specific function implementations
Example #[cfg(target_os = "windows")] fn get_temp_dir() Windows-specific temp directory implementation

cfg! Macro

The cfg! macro evaluates configuration conditions at runtime, returning a boolean value:

fn main() {
    // Check configuration at runtime
    if cfg!(target_os = "windows") {
        println!("Running on Windows");
    } else if cfg!(target_os = "macos") {
        println!("Running on macOS");
    } else if cfg!(target_os = "linux") {
        println!("Running on Linux");
    }
    
    // Combine multiple conditions
    if cfg!(all(target_arch = "x86_64", target_os = "linux")) {
        println!("Running on 64-bit Linux");
    }
    
    // Check for debug vs release builds
    if cfg!(debug_assertions) {
        println!("Debug build with assertions enabled");
    } else {
        println!("Release build");
    }
}

Built-in Configuration Options

Rust provides many built-in configuration options that you can use for conditional compilation:

🔍 Interactive Configuration Explorer

target_os

Target operating system

windows, macos, linux, android, ios

target_arch

Target CPU architecture

x86, x86_64, arm, aarch64

target_family

Operating system family

unix, windows

target_env

Target environment ABI

gnu, msvc, musl

debug_assertions

Debug assertions enabled

true (debug), false (release)

test

Building for tests

true (cargo test), false (normal build)

proc_macro

Procedural macro context

true (in proc macro), false (normal code)

feature = \"name\"

Custom feature flags

any string defined in Cargo.toml

Custom cfg

User-defined configuration

Set via --cfg flag or build.rs
Configuration Description Example Values
target_arch Target CPU architecture x86, x86_64, arm, aarch64
target_os Target operating system windows, macos, linux, android
target_family Operating system family unix, windows
target_env Target environment ABI gnu, msvc, musl
target_endian Target endianness little, big
target_pointer_width Target pointer width in bits 16, 32, 64
debug_assertions Debug assertions enabled true, false
test Building for tests true, false

Feature Flags

Feature flags allow you to optionally enable or disable parts of your code based on Cargo features:

# Cargo.toml
[package]
name = "my-crate"
version = "0.1.0"

[features]
default = ["json-support"]
json-support = ["serde", "serde_json"]
xml-support = ["quick-xml"]
async-support = ["tokio"]
full = ["json-support", "xml-support", "async-support"]

[dependencies]
serde = { version = "1.0", optional = true }
serde_json = { version = "1.0", optional = true }
quick-xml = { version = "0.22", optional = true }
tokio = { version = "1.0", optional = true }
// Use feature flags in code
#[cfg(feature = "json-support")]
use serde_json;

#[cfg(feature = "json-support")]
pub fn parse_json(input: &str) -> Result {
    serde_json::from_str(input)
}

#[cfg(feature = "xml-support")]
pub fn parse_xml(input: &str) -> Result {
    // XML parsing implementation
}

#[cfg(feature = "async-support")]
pub async fn async_process(data: Vec) -> Result {
    // Async processing implementation
    tokio::time::sleep(Duration::from_millis(100)).await;
    Ok(ProcessedData::new(data))
}

// Provide fallback for missing features
#[cfg(not(feature = "json-support"))]
pub fn parse_json(_input: &str) -> Result<(), &'static str> {
    Err("JSON support not enabled")
}

Boolean Logic in cfg

The cfg attribute supports boolean logic for complex conditions:

// AND logic - all conditions must be true
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn linux_x64_specific() {
    println!("This only runs on 64-bit Linux");
}

// OR logic - any condition can be true
#[cfg(any(target_os = "windows", target_os = "macos"))]
fn desktop_only() {
    println!("This runs on desktop platforms");
}

// NOT logic - condition must be false
#[cfg(not(target_os = "windows"))]
fn non_windows() {
    println!("This runs on everything except Windows");
}

// Complex combinations
#[cfg(all(
    any(target_os = "linux", target_os = "macos"),
    target_arch = "x86_64",
    feature = "advanced-features"
))]
fn complex_condition() {
    println!("Complex conditional compilation");
}

Custom Configuration Values

You can define custom configuration values using --cfg flags or build scripts:

Command Line Configuration

# Set custom cfg values via command line
cargo build --cfg custom_config
cargo build --cfg 'feature="experimental"'

# Multiple configurations
cargo build --cfg debug_mode --cfg 'version="1.0"'

Build Script Configuration

// build.rs
use std::env;

fn main() {
    // Set configuration based on environment
    if env::var("ENABLE_EXPERIMENTAL").is_ok() {
        println!("cargo:rustc-cfg=experimental");
    }
    
    // Conditional configuration based on target
    let target = env::var("TARGET").unwrap();
    if target.contains("embedded") {
        println!("cargo:rustc-cfg=embedded_target");
    }
    
    // Version-based configuration
    let version = env::var("CARGO_PKG_VERSION").unwrap();
    if version.starts_with("2.") {
        println!("cargo:rustc-cfg=version_2");
    }
}
// Use custom configuration in code
#[cfg(experimental)]
pub fn experimental_feature() -> Result {
    // Cutting-edge functionality
    Ok("Experimental result".to_string())
}

#[cfg(embedded_target)]
const BUFFER_SIZE: usize = 512; // Small buffer for embedded

#[cfg(not(embedded_target))]
const BUFFER_SIZE: usize = 8192; // Large buffer for desktop

#[cfg(version_2)]
pub fn new_api() -> ApiV2 {
    ApiV2::new()
}

#[cfg(not(version_2))]
pub fn new_api() -> ApiV1 {
    ApiV1::new()
}

Conditional Imports and Dependencies

Conditional compilation can be used with imports and dependencies:

// Conditional imports
#[cfg(target_family = "unix")]
use std::os::unix::fs::PermissionsExt;

#[cfg(target_os = "windows")]
use std::os::windows::fs::MetadataExt;

// Conditional module declarations
#[cfg(feature = "networking")]
pub mod network;

#[cfg(feature = "database")]
pub mod db;

// Platform-specific implementations
#[cfg(target_os = "linux")]
mod platform {
    pub fn get_system_info() -> String {
        std::fs::read_to_string("/proc/version")
            .unwrap_or_else(|_| "Unknown".to_string())
    }
}

#[cfg(target_os = "windows")]
mod platform {
    pub fn get_system_info() -> String {
        "Windows System".to_string()
    }
}

pub use platform::get_system_info;

Testing with Conditional Compilation

Conditional compilation is particularly useful for testing scenarios:

// Test-only code
#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_basic_functionality() {
        assert_eq!(add(2, 3), 5);
    }
    
    // Platform-specific tests
    #[cfg(target_os = "linux")]
    #[test]
    fn test_linux_specific() {
        // Linux-only test
    }
}

// Debug-only functionality
#[cfg(debug_assertions)]
pub fn debug_info() -> String {
    format!("Debug build at {}", file!())
}

// Mock implementations for testing
#[cfg(test)]
pub fn get_data() -> Vec {
    vec![1, 2, 3, 4, 5] // Test data
}

#[cfg(not(test))]
pub fn get_data() -> Vec {
    // Real implementation
    std::fs::read("data.bin").unwrap_or_default()
}

🦀 Rust-Specific Conditional Compilation Quirks

🔗 cfg vs cfg! Distinction

CFG_ATTR := "#[cfg(" PREDICATE ")]" # Compile-time CFG_MACRO := "cfg!(" PREDICATE ")" # Runtime check USAGE := ATTR | EXPR_CONTEXT
// Compile-time exclusion
#[cfg(target_os = "linux")]
fn linux_only() { }  // Function doesn't exist on other platforms

// Runtime boolean
if cfg!(target_os = "linux") {
    // This code exists on all platforms but only runs on Linux
}

🔗 Feature Flag Inheritance

FEATURE_DEP := "feature" "=" STRING FEATURE_CASCADE := ENABLED_FEATURES -> DEPENDENCY_FEATURES DEFAULT_FEATURES := "default" "=" FEATURE_LIST
# Feature dependencies are transitive
[features]
full = ["networking", "database"]
networking = ["tokio", "reqwest"]
database = ["sqlx"]  # Inherits from networking when full is enabled

Best Practices

Design Guidelines

  • Minimize Platform Differences: Use conditional compilation sparingly
  • Consistent APIs: Keep the same function signatures across platforms
  • Fallback Implementations: Provide defaults when features are disabled
  • Test All Configurations: Ensure all conditional branches are tested

Feature Flag Strategy

  • Use descriptive feature names
  • Group related functionality in features
  • Provide sensible defaults
  • Document feature dependencies clearly

🤖 For AI Coding Agents

Conditional Compilation Generation Instructions

When generating conditional code, follow these machine-readable specifications:

{
  "conditional_compilation": {
    "version": "1.0",
    "cfg_attributes": {
      "target_os": {
        "values": ["windows", "macos", "linux", "android", "ios"],
        "usage": "#[cfg(target_os = \"value\")]",
        "purpose": "Platform-specific code compilation"
      },
      "target_arch": {
        "values": ["x86", "x86_64", "arm", "aarch64"],
        "usage": "#[cfg(target_arch = \"value\")]", 
        "purpose": "Architecture-specific optimizations"
      },
      "feature": {
        "values": ["user_defined"],
        "usage": "#[cfg(feature = \"feature_name\")]",
        "purpose": "Optional feature compilation"
      },
      "test": {
        "values": ["true", "false"],
        "usage": "#[cfg(test)]",
        "purpose": "Test-only code inclusion"
      }
    },
    "boolean_operators": {
      "all": "Logical AND - all conditions must be true",
      "any": "Logical OR - any condition can be true",
      "not": "Logical NOT - condition must be false"
    },
    "template_patterns": {
      "platform_specific_function": {
        "pattern": "#[cfg(target_os = \"{OS}\")]\nfn {function_name}() -> {return_type} {\n    // {OS}-specific implementation\n}",
        "variables": ["OS", "function_name", "return_type"],
        "example": "#[cfg(target_os = \"linux\")]\nfn get_config_path() -> PathBuf {\n    PathBuf::from(\"/etc/myapp\")\n}"
      }
    }
  }
}

Code Generation Templates

Template #[cfg(target_os = "{OS}")]\nfn {func}() {{ {impl} }} #[cfg(target_os = "linux")]\nfn get_home_dir() {{ env::var("HOME") }}
{
  "validation_rules": {
    "platform_coverage": "All major platforms should have implementations",
    "fallback_required": "Always provide not() fallback for missing cases",
    "consistent_apis": "Function signatures must match across platforms",
    "feature_dependencies": "Document all feature flag dependencies"
  },
  "common_patterns": {
    "cross_platform_function": "Multiple cfg attributes for same function",
    "feature_optional_module": "Module only available with feature flag",
    "debug_only_code": "Code only in debug builds",
    "test_mocks": "Different implementations for test vs production"
  }
}

✅ Correct: Complete Platform Coverage

// ✅ VALID: All platforms covered with fallback
#[cfg(target_os = "windows")]
fn get_config_dir() -> PathBuf {
    PathBuf::from(r"C:\ProgramData\MyApp")
}

#[cfg(target_os = "macos")]
fn get_config_dir() -> PathBuf {
    PathBuf::from("/Library/Application Support/MyApp")
}

#[cfg(target_os = "linux")]
fn get_config_dir() -> PathBuf {
    PathBuf::from("/etc/myapp")
}

#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
fn get_config_dir() -> PathBuf {
    PathBuf::from("/tmp/myapp") // Generic fallback
}

❌ Incorrect: Missing Platform Coverage

// ❌ INVALID: Only covers some platforms
#[cfg(target_os = "linux")]
fn get_config_dir() -> PathBuf {
    PathBuf::from("/etc/myapp")
}

#[cfg(target_os = "windows")]
fn get_config_dir() -> PathBuf {
    PathBuf::from(r"C:\ProgramData")
}

// ❌ ERROR: What happens on macOS, BSD, etc.?
// This will cause compilation errors on unsupported platforms
← Crates Names → ← Back to Reference