Условная компиляция

В Rust есть специальный атрибут, #[cfg], который позволяет компилировать код в зависимости от флагов, переданных компилятору. Он имеет две формы:

fn main() { #[cfg(foo)] fn foo() {} #[cfg(bar = "baz")] fn bar() {} }
#[cfg(foo)]

#[cfg(bar = "baz")]

Над атрибутами конфигурации определены логические операции:

fn main() { #[cfg(any(unix, windows))] fn foo() {} #[cfg(all(unix, target_pointer_width = "32"))] fn bar() {} #[cfg(not(foo))] fn not_foo() {} }
#[cfg(any(unix, windows))]

#[cfg(all(unix, target_pointer_width = "32"))]

#[cfg(not(foo))]

Они могут быть как угодно вложены:

fn main() { #[cfg(any(not(unix), all(target_os="macos", target_arch = "powerpc")))] fn foo() {} }
#[cfg(any(not(unix), all(target_os="macos", target_arch = "powerpc")))]

Что же касается того, как включить или отключить эти флаги: если вы используете Cargo, то они устанавливаются в разделе [features] вашего Cargo.toml:

[features]
# по умолчанию, никаких дополнительных возможностей
default = []

# возможность «secure-password» зависит от пакета bcrypt
secure-password = ["bcrypt"]

Если вы определите такие возможности, Cargo передаст флаг в rustc:

--cfg feature="${feature_name}"

Совокупность этих флагов конфигурации (cfg) будет определять, какие из них будут активны, и, следовательно, какой код будет скомпилирован. Давайте рассмотрим такой код:

fn main() { #[cfg(feature = "foo")] mod foo { } }
#[cfg(feature = "foo")]
mod foo {
}

Если скомпилировать его с помощью cargo build --features "foo", то в rustc будет передан флаг --cfg feature="foo", и результат будет содержать модуль mod foo. Если скомпилировать его с помощью обычной команды cargo build, то никаких дополнительных флагов передано не будет, и поэтому, модуль mod foo будет отсутствовать.

cfg_attr

Вы также можете установить другой атрибут в зависимости от переменной cfg с помощью атрибута cfg_attr:

fn main() { #[cfg_attr(a, b)] fn foo() {} }
#[cfg_attr(a, b)]

Этот код будет равносилен атрибуту #[b], если в атрибуте cfg установлен флаг a, или «без атрибута» в противном случае.

cfg!

Расширение синтаксиса cfg! позволяет использовать данные виды флагов и в другом месте в коде:

fn main() { if cfg!(target_os = "macos") || cfg!(target_os = "ios") { println!("Think Different!"); } }
if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
    println!("Think Different!");
}

Значение флага будет заменено на true или false во время компиляции, в зависимости от настройки конфигурации.