Conditional Compilation

Both .NET and Rust are providing the possibility for compiling specific code based on external conditions.

In .NET it is possible to use the some preprocessor directives in order to control conditional compilation

#if debug
    Console.WriteLine("Debug");
#else
    Console.WriteLine("Not debug");
#endif

In addition to predefined symbols, it is also possible to use the compiler option DefineConstants to define symbols that can be used with #if, #else, #elif and #endif to compile source files conditionally.

In Rust it is possible to use the cfg attribute, the cfg_attr attribute or the cfg macro to control conditional compilation

As per .NET, in addition to predefined symbols, it is also possible to use the compiler flag --cfg to arbitrarily set configuration options

The cfg attribute is requiring and evaluating a ConfigurationPredicate

use std::fmt::{Display, Formatter};

struct MyStruct;

// This implementation of Display is only included when the OS is unix but foo is not equal to bar
// You can compile an executable for this version, on linux, with 'rustc main.rs --cfg foo=\"baz\"'
#[cfg(all(unix, not(foo = "bar")))]
impl Display for MyStruct {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_str("Running without foo=bar configuration")
    }
}

// This function is only included when both unix and foo=bar are defined
// You can compile an executable for this version, on linux, with 'rustc main.rs --cfg foo=\"bar\"'
#[cfg(all(unix, foo = "bar"))]
impl Display for MyStruct {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_str("Running with foo=bar configuration")
    }
}

// This function is panicking when not compiled for unix
// You can compile an executable for this version, on windows, with 'rustc main.rs'
#[cfg(not(unix))]
impl Display for MyStruct {
    fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result {
        panic!()
    }
}

fn main() {
    println!("{}", MyStruct);
}

The cfg_attr attribute conditionally includes attributes based on a configuration predicate.

#[cfg_attr(feature = "serialization_support", derive(Serialize, Deserialize))]
pub struct MaybeSerializableStruct;

// When the `serialization_support` feature flag is enabled, the above will expand to:
// #[derive(Serialize, Deserialize)]
// pub struct MaybeSerializableStruct;

The built-in cfg macro takes in a single configuration predicate and evaluates to the true literal when the predicate is true and the false literal when it is false.

if cfg!(unix) {
  println!("I'm running on a unix machine!");
}

See also:

Features

Conditional compilation is also helpful when there is a need for providing optional dependencies. With cargo "features", a package defines a set of named features in the [features] table of Cargo.toml, and each feature can either be enabled or disabled. Features for the package being built can be enabled on the command-line with flags such as --features. Features for dependencies can be enabled in the dependency declaration in Cargo.toml.

See also: