Attributes and Conditional Compilation

cfg!

The cfg!(predicate) macro is a built-in standard library macro that evaluates a conditional compilation predicate at compile time and expands to the boolean literal true or false. Unlike the #[cfg(...)] attribute, which strips unpicked items from the Abstract Syntax Tree, cfg!(...) is an ordinary boolean expression where every branch is still parsed and type-checked on all targets.

Critical Difference Between #[cfg(...)] and cfg!(...)

`#[cfg(windows)]` Attribute:
  #[cfg(windows)]
  { win32_only_api(); }     // Stripped from AST on Unix -> Does NOT need to exist on Unix!

`cfg!(windows)` Macro:
  if cfg!(windows) {
      win32_only_api();     // Expands to `if false { win32_only_api(); }` on Unix
  }                         // -> MUST still parse & type-check on Unix! (Dead-code eliminated later)
Property #[cfg(pred)] Attribute cfg!(pred) Macro
Syntactic role Attribute attached to an item, block, or statement Boolean expression (bool: true or false)
Inactive branch type-checked? No (removed from AST before type checking) Yes (must be valid, type-correct Rust on all platforms!)
Can use platform-only symbols (std::os::unix) in inactive branch? Yes No (fails to resolve on Windows!)
Can be stored in a const / let variable? No Yes (const IS_DEBUG: bool = cfg!(debug_assertions);)
Runtime branch cost 0 0 (constant-folded and dead-code eliminated by compiler)

Using cfg!(...) for Portable Configuration Selection

When both branches of an if/else expression use portable, cross-platform types (such as selecting buffer sizes, default file paths, or logging verbosity), cfg!(...) is cleaner than duplicating entire functions with #[cfg(...)] and #[cfg(not(...))] because the compiler guarantees that both branches remain syntactically and type-valid on every target:

const DEFAULT_BUFFER_SIZE: usize = if cfg!(debug_assertions) {
    1024
} else {
    64 * 1024
};

fn platform_line_ending() -> &'static str {
    if cfg!(windows) { "\r\n" } else { "\n" }
}

fn describe_environment() {
    let os_family = if cfg!(unix) {
        "unix-like"
    } else if cfg!(windows) {
        "windows"
    } else {
        "other"
    };

    let pointer_bits = if cfg!(target_pointer_width = "64") {
        64
    } else {
        32
    };

    println!(
        "family = {}, bits = {}, default_buf = {} B, newline_len = {}",
        os_family,
        pointer_bits,
        DEFAULT_BUFFER_SIZE,
        platform_line_ending().len()
    );
}

fn main() {
    describe_environment();
}
family = unix-like, bits = 64, default_buf = 1024 B, newline_len = 1

Why Platform-Specific APIs Require #[cfg] Instead of if cfg!

If you call a Unix-only function (like std::os::unix::fs::MetadataExt::ino) inside if cfg!(unix) { ... }, compilation will fail on Windows because std::os::unix does not exist on Windows targets and if false { ... } is still type-checked. Always use #[cfg(unix)] whenever the code inside references symbols or types that only exist when the predicate holds.