env! (și option_env!) în Rust

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.

Sintaxă
const VERSIUNE: &str = env!("CARGO_PKG_VERSION");   // eroare de compilare dacă lipsește
const X: Option<&str> = option_env!("NUME");        // None dacă lipsește
env!("NUME", "mesaj de eroare propriu")

Ce face env! (și option_env!)

env!("NUME") se înlocuiește, la compilare, cu valoarea variabilei de mediu NUME. Dacă variabila nu există, compilarea eșuează. option_env!("NUME") dă în schimb un Option<&'static str>, cu None când variabila lipsește.

Cargo setează la compilare multe variabile utile: CARGO_PKG_NAME, CARGO_PKG_VERSION, CARGO_PKG_AUTHORS, CARGO_PKG_DESCRIPTION (din Cargo.toml) și CARGO_MANIFEST_DIR (directorul proiectului). Așa un program își poate afișa versiunea fără să o scrii de două ori.

Valoarea este „înghețată” în executabil: schimbarea variabilei după compilare nu are efect. Pentru variabile citite când rulează programul (de exemplu HOME sau o configurare) folosește std::env::var("NUME").

Exemple

Exemplul 1: option_env!: variabile care pot lipsi

Exemplul este compilat direct cu rustc, fără Cargo, deci CARGO_PKG_VERSION nu există și option_env! dă None. BUILD_ID nu a fost setată nici ea.

const VERSIUNE: Option<&str> = option_env!("CARGO_PKG_VERSION");
const BUILD: &str = match option_env!("BUILD_ID") {
    Some(id) => id,
    None => "local",
};

fn main() {
    println!("Versiune: {}", VERSIUNE.unwrap_or("necunoscută (compilat fără Cargo)"));
    println!("Build: {BUILD}");
}

Rezultat

Versiune: necunoscută (compilat fără Cargo)
Build: local

Exemplul 2: env! într-un proiect Cargo

Cargo setează variabilele din Cargo.toml. Numele, versiunea, descrierea și directorul proiectului sunt incluse în executabil la compilare.

// src/main.rs; în Cargo.toml: name = "catalog", version = "1.2.0",
// description = "Catalog electronic pentru note"
fn main() {
    println!("{} v{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
    println!("{}", env!("CARGO_PKG_DESCRIPTION"));
    let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
    println!("proiect: {}", dir.file_name().unwrap().display());
}

$ cargo run -q

Rezultat în terminal

catalog v1.2.0
Catalog electronic pentru note
proiect: catalog

Exemplul 3: Variabilă lipsă: eroare la compilare

CHEIE_API nu este setată, deci env! oprește compilarea cu mesajul dat ca al doilea argument.

fn main() {
    let cheie = env!("CHEIE_API", "setează CHEIE_API înainte de compilare");
    println!("{} caractere", cheie.len());
}

Mesajul compilatorului (programul nu se compilează)

error: setează CHEIE_API înainte de compilare
 --> main.rs:2:17
  |
2 |     let cheie = env!("CHEIE_API", "setează CHEIE_API înainte de compilare");
  |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 1 previous error

Greșeli frecvente

  • Folosești env!("HOME") crezând că citește directorul utilizatorului care rulează programul. Citește valoarea de pe calculatorul care a compilat; folosește std::env::var.
  • Pui secrete (parole, chei API) în executabil cu env!: oricine are executabilul le poate extrage.
  • Folosești env!("CARGO_PKG_VERSION") într-un fișier compilat fără Cargo: compilarea eșuează. Folosește option_env! dacă codul trebuie să meargă și fără Cargo.

Comenzi înrudite

  • include_str!: Citește un fișier text la compilare și îl pune în program ca &’static str. Fișierul nu mai este necesar la rulare.
  • cfg!: Evaluează la compilare o condiție de configurare (sistem de operare, mod debug, feature Cargo) și o transformă în true sau false.
  • concat!: Lipește literali (texte, numere, bool, char) într-un singur &’static str, la compilare, fără niciun cost la rulare.
  • Cargo.toml: Fișierul de configurare al unui proiect Rust (în format TOML): numele și versiunea, ediția, dependențele, funcționalitățile și setările de compilare.