Fundamentals 8 min read

Master Conditional Compilation in Rust with cfg_if: A Practical Guide

This article explains how the Rust cfg_if crate simplifies conditional compilation by providing a macro that replaces nested #[cfg] attributes with clear if‑else or match structures, shows step‑by‑step usage, advanced patterns, and compares cfg_if to raw #[cfg] for readability and maintainability.

BirdNest Tech Talk
BirdNest Tech Talk
BirdNest Tech Talk
Master Conditional Compilation in Rust with cfg_if: A Practical Guide

cfg_if is a Rust utility crate that streamlines conditional‑compilation code by offering the cfg_if! macro, which lets developers write compile‑time configuration logic more cleanly.

Adding the Dependency

First, add the crate to Cargo.toml:

cfg-if = "1.0.0"

Basic Usage

Import the macro and write conditional blocks:

use cfg_if::cfg_if;

cfg_if! {
    if #[cfg(unix)] {
        fn foo() { /* unix‑specific functionality */ }
    } else if #[cfg(windows)] {
        fn foo() { /* windows‑specific functionality */ }
    } else {
        fn foo() { /* fallback implementation */ }
    }
}

The macro expands at compile time into an equivalent if‑else chain or match expression, so there is no runtime overhead.

Advantages of cfg_if

Simplified syntax: replaces verbose nested #[cfg()] attributes with a readable macro.

Reduces errors: ensures an else branch is present, avoiding missing cases.

Composable: multiple conditions can be combined easily.

Advanced Syntax

Complex conditions can be expressed directly in the macro:

cfg_if! {
    if #[cfg(all(unix, target_pointer_width = "32"))] {
        // 32‑bit unix systems
    } else if #[cfg(all(unix, target_pointer_width = "64"))] {
        // 64‑bit unix systems
    } else if #[cfg(windows)] {
        // windows systems
    } else {
        // other systems
    }
}

The Built‑in #[cfg()] Attribute

#[cfg()]

is Rust’s native attribute for conditional compilation. Examples:

#[cfg(unix)]
fn unix_only() { /* compiled only on Unix */ }

#[cfg(windows)]
fn windows_only() { /* compiled only on Windows */ }

Common configuration options include operating system ( unix, windows, macos, linux), architecture ( x86, x86_64, arm), feature flags defined in Cargo.toml, and compiler version ( rustc_1_40).

Logical Operators

Multiple conditions can be combined with logical operators:

#[cfg(all(unix, target_pointer_width = "64"))]
fn unix_64bit_function() { /* only on 64‑bit Unix */ }

#[cfg(any(windows, macos))]
fn windows_or_mac_function() { /* on Windows or macOS */ }

#[cfg(not(debug_assertions))]
fn release_only() { /* only in release mode */ }

Typical Use Cases

Module‑level conditional compilation:

#[cfg(feature = "advanced")]
mod advanced_features {
    // compiled only when the "advanced" feature is enabled
}

Conditional imports:

#[cfg(unix)]
use std::os::unix::fs::MetadataExt;

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

Runtime‑style checks with cfg!:

if cfg!(debug_assertions) {
    println!("Debug mode");
} else {
    println!("Release mode");
}

Custom Feature Flags

Define a flag in Cargo.toml:

[features]
my_feature = []

Then use it in code:

#[cfg(feature = "my_feature")]
fn feature_specific_function() {
    // ...
}

Full List of cfg Attributes

all

: all conditions must be true. any: any condition true is sufficient. not: condition must be false. target_os: target operating system. target_arch: target architecture. target_feature: specific CPU feature. target_endian: byte order. target_pointer_width: pointer width. target_env: target environment. target_vendor: target vendor. feature: compile‑time feature flag. debug_assertions: whether debug assertions are enabled. test: compile only in test mode.

These attributes can be freely combined, for example:

#[cfg(all(unix, not(any(target_os = "macos", target_os = "ios"))))]

Using cfg_attr for Conditional Attributes

The cfg_attr macro applies another attribute only when a condition holds:

#[cfg_attr(feature = "nightly", feature(some_unstable_feature))]
... 

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
struct MyStruct {
    // fields ...
}

All these configurations are evaluated at compile time, incurring no runtime cost.

cfg_if vs #[cfg()]

#[cfg()]

is built‑in and requires no extra dependency.

For simple conditions, #[cfg()] is more direct.

For complex, nested conditions, cfg_if! often yields clearer, more maintainable code.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendRustmacroconditional compilationcfg_if
BirdNest Tech Talk
Written by

BirdNest Tech Talk

Author of the rpcx microservice framework, original book author, and chair of Baidu's Go CMC committee.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.