cfg! în Rust

Evaluează la compilare o condiție de configurare (sistem de operare, mod debug, feature Cargo) și o transformă în true sau false.

Sintaxă
cfg!(target_os = "linux")          // true sau false, la compilare
cfg!(windows)  cfg!(unix)  cfg!(test)  cfg!(debug_assertions)
cfg!(target_pointer_width = "64")
cfg!(feature = "export")
cfg!(any(a, b))  cfg!(all(a, b))  cfg!(not(a))

Ce face cfg!

cfg!(conditie) se înlocuiește la compilare cu true sau false, după configurarea compilării. Condițiile sunt aceleași ca la atributul #[cfg(...)]: target_os = "windows", unix, windows, target_arch = "aarch64", debug_assertions, test, feature = "nume", combinate cu any, all și not.

Diferența față de #[cfg]: cu cfg!, ambele ramuri ale unui if sunt compilate, deci trebuie să fie cod valid pe orice sistem; doar valoarea condiției diferă. Cu #[cfg], codul care nu corespunde este eliminat înainte de compilare, deci poate folosi funcții care există doar pe un anumit sistem.

Folosește cfg! pentru diferențe mici de comportament (un mesaj, o valoare implicită) și #[cfg] când codul nu s-ar compila pe celelalte sisteme.

Ce returnează

true sau false, constantă la compilare.

Exemple

Exemplul 1: Informații despre compilare

Exemplul a fost compilat și rulat pe un Mac cu procesor Apple (macOS, aarch64), fără optimizări, deci debug_assertions este activ. Pe Linux sau Windows, rândurile ar arăta altfel.

fn main() {
    let sistem = if cfg!(target_os = "windows") {
        "Windows"
    } else if cfg!(target_os = "macos") {
        "macOS"
    } else if cfg!(target_os = "linux") {
        "Linux"
    } else {
        "alt sistem"
    };
    println!("Sistem: {sistem}, Unix: {}", cfg!(unix));
    println!("64 de biți: {}", cfg!(target_pointer_width = "64"));
    println!("ARM pe 64 de biți: {}", cfg!(target_arch = "aarch64"));
    println!("mod debug: {}", cfg!(debug_assertions));
}

Rezultat

Sistem: macOS, Unix: true
64 de biți: true
ARM pe 64 de biți: true
mod debug: true

Exemplul 2: Valoare implicită diferită pe sisteme diferite

Ambele ramuri sunt compilate pe orice sistem, pentru că sunt simple texte. Pe macOS și Linux se alege calea de tip Unix.

fn director_config() -> &'static str {
    if cfg!(windows) {
        r"C:\ProgramData\catalog"
    } else {
        "/etc/catalog"
    }
}

fn main() {
    println!("Configurarea se caută în {}", director_config());
    if cfg!(debug_assertions) {
        println!("(versiune de dezvoltare: verificări suplimentare active)");
    }
}

Rezultat

Configurarea se caută în /etc/catalog
(versiune de dezvoltare: verificări suplimentare active)

Greșeli frecvente

  • Folosești if cfg!(windows) { functie_doar_windows() }: pe Linux ramura este tot compilată, iar funcția nu există, deci build-ul eșuează. Folosește #[cfg(windows)] pe funcție sau pe bloc.
  • Verifici cfg!(feature = "x") pentru o funcționalitate nedeclarată în [features]: compilatorul avertizează unexpected `cfg` condition value.

Comenzi înrudite

  • atribute (#[derive], #[cfg], #[allow]): Informații pentru compilator scrise cu #[…] deasupra unui element: generează cod (derive), aleg codul compilat (cfg) sau schimbă avertismentele.
  • env! (și option_env!): Citește o variabilă de mediu la compilare și o pune în program ca &’static str. Pentru valori la rulare se folosește std::env::var.
  • assert! și debug_assert!: Verifică o condiție la rulare și dă panică dacă este falsă. debug_assert! face verificarea doar în modul debug, nu și în release.
  • line!, file! și column!: Dau poziția din codul sursă unde sunt scrise: numărul liniei, coloana și numele fișierului. Utile în mesaje de jurnal și erori.