Color theme Dark mode Font size Content width

Rust Compiler Umgebungsvariablen

Published on 09/30/2020

Rust ist eine wunderbare Programmiersprache mit einem gut durchdachten Compiler. Das Rust Team hat sich die BĂŒrde auferlegt, den Compiler so “schlau” wie möglich zu machen um so viele Fehler schon wĂ€hrend dem kompilieren des Codes zu erkennen. Außerdem wird Rust mit einem mĂ€chtigen Tool namens cargo ausgeliefert. Cargo ist Ă€hnlich wie npm oder maven und stellt verschiedenste Funktionen bereit, zum Beispiel zum bauen, testen, oder veröffentlichen von Rust Code. In diesem Blog-Beitrag möchte ich auf ein Build Feature von Cargo eingehen: Environment Variables.

Source Code

Der gesamte Source Code fĂŒr diesen Blog-Beitrag kann auf GitHub gefunden werden: github.com/kevingimbel/rust-blog-code/env-example.

Einige Code Beispiele können direkt ausgefĂŒhrt werden. Anweisung wie z.B. cargo run --examples ding in diesem Beitrag fĂŒhren die verschiedenen Code Beispiele im examples Ordner aus. Um diese zu verwenden, folgende Schritten ausfĂŒhren:

  1. Terminal öffnen
  2. Repo clonen
  3. cd env-example
  4. Befehle ausfĂŒhren wenn sie im Beitrag vorkommen, z.B. cargo run --example maybe-fn

env!() vs. std::env::var()

Umgebungsvariablen können auf zwei Arten ausgelesen werden:

  • env!() Makro
  • std::env::var() Funktion

Der wichtige Unterschied ist, dass env!() den Wert zur Kompilierzeit ausliest und ins Binary schreibt, wÀhrend std::env() immer den Wert aus der Umgebung liest. Das lÀsst sich am folgenden Beispiel gut erkennen:

fn main() {
    let env_macro = env!("MY_VAR");
    let env_fn = std::env::var("MY_VAR");

    println!("env_macro = {}", env_macro);
    println!("env_fn = {}", env_fn);
}

Wenn wir diesen Code kompilieren bekommen wir einen Fehler:

$ cargo build 
   Compiling env-example v0.1.0 (rust-blog-code/env-example)
error: environment variable `MY_VAR` not defined
 --> src/main.rs:2:21
  |
2 |     let env_macro = env!("MY_VAR");
  |                     ^^^^^^^^^^^^^^
  |
  = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

Es gibt nun zwei Möglichkeiten:

  • wir setzen die Environment Variable mit export MY_VAR=42
  • wir definieren Standardwerte

ZunÀchst setzen wir die Variable und können den Code dann kompilieren, spÀter in diesem Beitrag schauen wir uns auch Standardwerte an.

$ export MY_VAR=42
$ cargo build

Anschließend können wir das Binary ausfĂŒhren (hier env-example gennant)

$ ./target/debug/env-example
env_macro = 42
env_fn = 42

Beide Werte sind 42. Jetzt setzen wir die Environment Variable neu:

export MY_VAR=8
$ ./target/debug/env-example
env_macro = 42
env_fn = 8

Aha! env_macro hat immer noch den Wert, den die Variable zur Kompilierzeit hatte.

Variablen zur Kompilierzeit

cargo stellt eine Handvoll Umgebungsvariablen wĂ€hrend der Kompilierzeit bereit. Diese Variablen können z.B. benutzt werden um die aktuelle Version aus der Cargo.toml Datei auszulesen und in unserem Binary zu hinterlegen. Nehmen wir das env-example script und fĂŒgen eine neue Zeile hinzu, die die Aktuelle version aus der Cargo.toml ausliest.

fn main() {
    let version = env!("CARGO_PKG_VERSION");
    let env_macro = env!("MY_VAR");
    let env_fn = std::env::var("MY_VAR").unwrap();

    println!("env-example v{}", version);
    println!("env_macro = {}", env_macro);
    println!("env_fn = {}", env_fn);
}

Wir mĂŒssen die neue Variable CARGO_PKG_VERSION nicht selbst setzen. Cargo setzt diese Variable fĂŒr uns.

Wir kompilieren nun den Code und fĂŒhren das Binary aus.

$ cargo build
$ ./target/debug/env-example
env-example v0.1.0
env_macro = 8
env_fn = 8

Der Wert 0.1.0 kommt nun direkt aus der Cargo.toml Datei! Wir können auch den Namen unseres Programms Cargo.toml auslesen:

fn main() {
    let version = env!("CARGO_PKG_VERSION");
    let name = env!("CARGO_PKG_NAME");
    let env_macro = env!("MY_VAR");
    let env_fn = std::env::var("MY_VAR").unwrap();

    println!("name: {}", name);
    println!("version: {}", version);
    println!("env_macro = {}", env_macro);
    println!("env_fn = {}", env_fn);
}

Kompilieren und ausfĂŒhren (-q unterdrĂŒckt die Ausgabe von cargo build):

$ cargo build -q && ./target/debug/env-example
name: env-example
version: 0.1.0
env_macro = 8
env_fn = 8

Standardwerte

std::env::var() bieten Möglichkeiten Standardwerte zu setzen.

fn main() {
    let maybe_fn = std::env::var("MY_VAR").unwrap_or(22);
    println!("maybe_fn = {}", maybe_fn);
} 
$ cargo build -q && ./target/debug/env-example

env!() bietet diese Möglichkeit nicht, es muss ein Wert gesetzt sein. Wir Können allerdings eine eigene Fehlermeldung als zweiten Parameter angeben.

fn main() {
    let maybe_macro = env!("MY_VAR", "Bitte MY_VAR setzen, z.B. mit export MY_VAR=21");
    println!("maybe_macro = {}", maybe_macro);
} 

cargo run --examples maybe-macro

Wenn wir den Code ausfĂŒhren bekommen wir folgenden Fehler:

$ cargo run --example maybe-macro
   Compiling env-example v0.1.0 (rust-blog-code/env-example)
error: Bitte MY_VAR setzen, z.B. mit export MY_VAR=21
 --> examples/maybe-macro.rs:2:23
  |
2 |     let maybe_macro = env!("MY_VAR", "Bitte MY_VAR setzen, z.B. mit export MY_VAR=21");
  |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |

cargo run --examples maybe-macro

Zusammenfassung

  • cargo stellt Umgebungsvariablen zur Kompilierzeit bereit
  • Umgebungsvariablen können mit dem env!() Makro in unseren Code eingebettet werden
  • std::env::var() liest Variablen zur AusfĂŒhrungszeit aus
  • env!() liest Variablen zur Kompilierzeit

Categories

Tags

Like this?

If this content has helped you or your company, consider making a donation to a good cause.

For example, consider donating to the following causes:

A Selfie of me

Kevin Gimbel

is a DevOps Engineer and avid (Video) Gamer. He’s also interested in Sci-Fi, Cyberpunk, and dystopian books.

Wearing a mask is a good idea!

You can find out more about me if you wish.