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

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


# #![allow(unused_variables)]
#fn main() {
#[cfg(foo)]
# fn foo() {}

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

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


# #![allow(unused_variables)]
#fn main() {
#[cfg(any(unix, windows))]
# fn foo() {}

#[cfg(all(unix, target_pointer_width = "32"))]
# fn bar() {}

#[cfg(not(foo))]
# fn not_foo() {}
#}

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


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

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

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

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

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

--cfg feature="${feature_name}"

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


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

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

cfg_attr

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


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

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

cfg!

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


# #![allow(unused_variables)]
#fn main() {
if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
    println!("Think Different!");
}
#}

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