Rust Compiler Umgebungsvariablen

Written by Kevin Gimbel on , ­čŹ┐ 5 min. read

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

Weiterf├╝hrende Links

Hi, I'm Kevin!

I'm a DevOps Engineer with a passion for on automation and monitoring. Before shifting into DevOps and cloud computing I worked as Front-End Developer, which is still a hobby and field of interest for me.

Picture of Kevin Gimbel, in a tiny mirror

I'm very passionated about a variety of games - digital, boardgames, and pen & paper; and also interested in Sci-Fi, Cyberpunk, and dystopian books. You can find out more on the about page.