Skip to content

← Modules, Visibility, Testing and Docs step 12 of 22

Medium Framework

#[cfg], cfg!, cfg_attr and the new cfg_select!

#[cfg(test)] is how most people meet conditional compilation, and it is the least of what the mechanism does. The same predicate language drives platform support, feature gates and MSRV shims across the whole ecosystem.

Two functions are graded.

pub fn eval_cfg(predicate: &str, active: Vec<String>) -> Result<bool, String>
pub fn pointer_class() -> &'static str

The predicate language

It is small, and you are implementing all of it.

name                     a bare option, e.g. unix, windows, test
key = "value"            e.g. target_os = "linux", feature = "serde"
all(p, q, …)             conjunction
any(p, q, …)             disjunction
not(p)                   negation, exactly one argument
true / false             literals

active is the set of options that hold, written in the same syntax (target_os="linux"). Whitespace is not part of the grammar — normalise it away on both sides, so target_os = "linux" and target_os="linux" are the same predicate.

Empty groups are easy to get backwards: all() is true and any() is false. That is not a quirk. “Every one of no conditions holds” is vacuously true; “at least one of no conditions holds” cannot be.

Return Err with exactly these messages:

situation message
empty or all-whitespace predicate empty predicate
parentheses that do not balance unbalanced parentheses
not() with zero or more than one argument not() takes exactly one predicate
a leaf that is neither an identifier nor key="value" bad predicate: <leaf> — the leaf after whitespace removal

The one that removes code, and the one that does not

This is the distinction learners get wrong constantly, and it matters.

#[cfg(…)] is an attribute. It deletes code before type-checking. An item that is cfg’d out can contain complete nonsense — reference types that do not exist, call functions that were never written — and the compile still succeeds, because the compiler never looks inside.

cfg!(…) is a macro. It evaluates to a bool at compile time and deletes nothing. Both branches of if cfg!(unix) { … } else { … } must type-check on every platform. Reach for cfg! when the two branches are both valid everywhere, and #[cfg] when they are not.

cfg_select!, new in 1.95

Stabilised recently enough that almost no tutorial covers it, cfg_select! is std’s replacement for the cfg-if crate — first match wins, _ as the fallback, usable in item and expression position:

cfg_select! {
    target_pointer_width = "64" => { pub fn f() -> u8 { 64 } }
    _                           => { pub fn f() -> u8 { 0 } }
}

Use it to write pointer_class, returning "64-bit", "32-bit", "16-bit" or "unknown". The test does not hardcode an answer — the harness derives what it should be from size_of::<usize>() on the machine you are compiling on, so the case is portable.

The starter has the arms in the wrong order: _ first, which always matches, so every arm below it is dead. rustc’s unreachable_cfg_select_ predicates says so, -D warnings makes it fatal, and moving one line fixes it. First-match-wins means an over-broad early arm silently swallows the specific ones; put the narrow predicates first, always.

If no arm matches and there is no _, cfg_select! is a compile error, not a silent nothing. That is usually what you want.

A note on the active set here

Under this harness the compile is plain rustc -O, so the active cfg set notably does not include test (that only exists under rustc --test) or debug_assertions (that is off under -O). Which is exactly why this item asks you to write an evaluator against a supplied option set instead of asking you to observe the compiler’s own.

Two lints worth knowing: clippy::non_minimal_cfg catches all(unix) — a one-element conjunction that should just be unix. And rustc’s unexpected_cfgs catches #[cfg(target_os = "linus")], a typo that would otherwise silently delete your code forever; the old clippy::mismatched_target_os was folded into it and uplifted into the compiler.