🎓 A1Learner
🔍

Rust Conditional Compilation

Conditional compilation is the compiler's choice to keep only the code that fits the target. One source file can hold the pieces that differ between machines.

Two versions of one function; one survives

Use #[cfg(...)] above an item to compile that item only when a condition is true. If the condition is false, the item is removed before the remaining checks. So it may even call functions that only exist on that platform. Common conditions name the target: target_os = "linux", target_arch = "x86_64", target_pointer_width = "64". On this Linux machine, the first platform() wins:

#[cfg(target_os = "linux")]
fn platform() -> &'static str { "Linux" }
#[cfg(not(target_os = "linux"))]
fn platform() -> &'static str { "not Linux" }
println!("platform = {}", platform());

cfg! asks inside normal code

Use the cfg!(...) macro when you need the answer inside normal code. It becomes the true or false literal. It removes nothing. So both branches must compile on every platform:

println!("is linux = {}", cfg!(target_os = "linux"));
println!("64-bit = {}", cfg!(target_pointer_width = "64"));

all, any and not combine conditions

all needs every part true, any needs one part true, and not flips one part:

#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn combo() -> &'static str { "linux on x86_64" }
#[cfg(not(all(target_os = "linux", target_arch = "x86_64")))]
fn combo() -> &'static str { "something else" }
println!("{}", combo());

#[cfg(test)] code exists only while testing

Use #[cfg(test)] for code that exists only while testing. Use #[cfg(feature = "name")] for code behind a Cargo feature. The Cargo Dependencies lesson turns features on and off:

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() { assert_eq!(2 + 2, 4); }
}

cfg_attr applies an attribute only sometimes

Use #[cfg_attr(condition, attribute)] to apply another attribute only when the condition holds. Here Meter earns Debug printing only in debug builds — compile this in a release build and the {m:?} print fails, because the derive was never applied:

#[cfg_attr(debug_assertions, derive(Debug))]
struct Meter { ticks: u32 }
let m = Meter { ticks: 3 };
println!("{m:?}");

A whole module can be gated

cfg works on a mod line too. Every item inside joins the build, or none of them do:

#[cfg(unix)]
mod unix_bits {
    pub fn mark() -> &'static str { "unix build" }
}
#[cfg(not(unix))]
mod unix_bits {
    pub fn mark() -> &'static str { "other build" }
}
println!("mark = {}", unix_bits::mark());

cfg_select! picks one branch

Newer compilers add a macro that picks among several cfg branches at once. The first true branch wins, and _ is the fallback:

let kind = cfg_select! {
    unix => "unix",
    windows => "windows",
    _ => "other",
};
println!("kind = {kind}");

Example

#[cfg(target_os = "linux")]
fn platform() -> &'static str { "Linux" }
#[cfg(not(target_os = "linux"))]
fn platform() -> &'static str { "not Linux" }
fn main() {
    println!("platform = {}", platform());
    println!("is linux = {}", cfg!(target_os = "linux"));
    println!("64-bit = {}", cfg!(target_pointer_width = "64"));
    println!("testing = {}", cfg!(test));
}

Key points

  • Your own condition names are not built in.
  • Pass one with rustc --cfg some_condition.
  • Or let a build script print a cargo:rustc-cfg line.
  • Best of all, prefer a Cargo feature.
  • A feature is the documented route other Rust tools understand
  • The finished program holds only the winning pieces — no platform test is left inside it.

In this example, two versions of platform() are written. The #[cfg] lines choose between them before the program is built. On a Linux computer the first version wins, so platform = Linux prints. The cfg! lines ask questions inside normal code. is linux is true. 64-bit is true on a 64-bit target. And testing is false. This is a normal run, not cargo test. On a Windows machine the second platform() would win instead. And is linux would print false. The code you receive is the version for your machine.

Note

cfg is decided while compiling, never while the program runs.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

What is the difference between #[cfg(...)] and cfg!(...)?