Programmieren in Rust

Compiler und Erstellungswerkzeuge

Inhaltsverzeichnis

  1. Direkter Aufruf des Compilers
  2. Ein Programm erstellen
  3. Abhängigkeiten
  4. Aufräumen
  5. Dokumentation
  6. Links

Direkter Aufruf des Compilers

Für gewöhnlich geschieht die Bedienung des Rust-Compilers rustc über das Erstellungswerkzeug cargo. Stattdessen kann man den Compiler auch direkt aufrufen. Zunächst erstellt man dafür in einem Verzeichnis die Datei »main.rs«, in welche das Programm

fn main() {}

geschrieben wird. Im selben Verzeichnis öffnet man ein Terminal und führt die folgenden Befehle aus:

> rustc main.rs -o prog
> ./prog

Der erste Befehl kompiliert das Programm, wobei die erzeugte ausführbare Datei den Namen prog bekommen soll. Der zweite Befehl ruft dieses Programm aus.

Ort von rustc (Linux)

Der Befehl rustc wird nur dann gefunden, wenn sich die Datei rustc in einem Verzeichnis befindet, welches in der Umgebungsvariable PATH enthalten ist. Unter Linux kann man diese Variable so auslesen:

> echo $PATH

Der Befehl whereis findet das Verzeichnis, in welchem sich ein Programm befindet. Bei Benutzerkonto-lokaler Installation des Compilers bekommt man:

> whereis rustc
rustc: /home/Benutzer/.cargo/bin/rustc

Nach frischer Installation des Compilers muss man sich unter Umständen einmal neu in das Benutzerkonto einloggen, damit das Verzeichnis zu PATH hinzugefügt wird. Nach dem Login wird automatisch die .profile oder .bash_profile genannte Datei im Verzeichnis /home/Benutzer/ ausgeführt. Dieses Shell-Skript kann das Verzeichnis durch den Befehl

export PATH="$HOME/.cargo/bin:$PATH"

hinzufügen. Bei globaler Installation ist dies nicht notwendig, da sich rustc im Verzeichnis /usr/bin/ befinden wird, welches standardmäßig in PATH enthalten ist.

Die Reihenfolge der Verzeichnisse in PATH ist signifikant. Wird ein Programm in einem Verzeichnis nicht gefunden, wird es im nächsten Verzeichnis gesucht. Somit kann es bei Mehrfachinstallation vorkommen, dass die globale Installation durch die lokale überschattet wird, da die lokale in PATH zuerst kommt.

Ein Programm erstellen

Anstelle des direkten Aufrufs des Compilers benutzt man eigentlich allermeistens das komfortable Erstellungswerkzeug Cargo. Eine wesentliche Aufgabe von Cargo ist die automatische Beschaffung und Kompilierung von benötigten Bibliotheken. Daneben enthält Cargo aber auch viele weitere Teilwerkzeuge.

Zunächst sei erläutert, wie die Erstellung eines Programms abläuft. In einem Verzeichnis öffnet man ein Terminal und führt die folgenden Befehle aus:

> cargo new prog0
> cd prog0

Der erste Befehl erzeugt ein neues Verzeichnis prog0. Die vom Compiler erzeugte ausführbare Datei wird standardmäßig ebenfalls prog0 heißen. Der zweite Befehl wechselt in dieses Verzeichnis. Man findet die folgende Struktur vor:

prog0/
├─ src/
│  └─ main.rs
└─ Cargo.toml

Das Verzeichnis src enthält die Quelltexte. Die Benennung src ist eine Abkürzung für source, das englische Wort für Quelle. Zunächst liegt dort nur die Datei main.rs vor, welche die Hauptfunktion main enthält. Die Datei Cargo.toml dient zur Eintragung von Konfigurationen, Informationen und Abhängigkeiten.

Zur Kompilierung führt man nun der Reihe nach die folgenden Befehle aus:

> cargo check
> cargo build
> cargo build --release

Der erste Befehl prüft lediglich die Korrektheit des Programms, erstellt aber keine ausführbare Datei. Der zweite Befehl erstellt zusätzlich die ausführbare Datei im Debug-Modus. Mit der Option --release wird die ausführbare Datei stattdessen im Release-Modus erstellt, bei dem Optimierungen eingeschaltet sind. Wir finden nun die folgende Verzeichnis-Struktur vor:

prog0/
├─ src/
│  └─ main.rs
├─ target/
│  ├─ debug/
│  └─ release/
├─ Cargo.lock
└─ Cargo.toml

Von der ausführbaren Datei gibt es zwei Varianten. Die im Debug-Modus erstellte Datei befindet sich im Verzeichnis target/debug, die im Relase-Modus erstellte in target/release.

Zusätzlich gibt es einen Komfort-Befehl, der das Programm nach der Erstellung sogleich ausführt:

> cargo run
> cargo run --release

Kommandozeilen-Argumente kommen hinter einen doppelten Strich. Betrachten wir dazu dieses Programm:

fn main() {
    let argv: Vec<String> = std::env::args().collect();
    println!("{:?}", argv);
}

Der Aufruf

> cargo run -- arg1 arg2

ist dann gleichbedeutend mit:

> cargo build
> ./target/debug/prog0 arg1 arg2

Zu bemerken ist, dass sich cargo in allen Unterverzeichnissen aufrufen lässt. Das Arbeitsverzeichnis des Programms prog0 ist hierbei das Verzeichnis, in welchem cargo aufgerufen wurde.

Abhängigkeiten

Die Datei Cargo.lock enthält alle transitiven Abhängigkeiten und ihre kryptografischen Hashwerte. Transitiv bedeutet hier, dass wenn Bibliothek A von B und B von C abhängig ist, dann auch A von C abhängig ist. Da Bibliotheken wieder abhängig von anderen Bibliotheken sein können, erhält man so einen zykelfreien Graph von Abhängigkeiten.

Wenn man also wissen möchte, wie viele Abhängigkeiten eine Software tatsächlich hat, lohnt sich ein Blick in die Datei Cargo.lock. Zu beachten ist aber, dass darin nur Crates zu finden sind. Bibliotheken, die über das C-ABI angebunden wurden, sind darin natürlich nicht enthalten.

Aufräumen

Der von Cargo erzeugte Ordner target sollte nicht im Backup gespeichert werden, zumal er sehr groß werden kann. Man führt in der Verzeichniswurzel den Befehl

find -name "target"
aus, daraufhin werden alle Verzeichnisse mit target aufgelistet. In diesen Verzeichnissen führt man jeweils den Befehl
cargo clean
aus. Das Verzeichnis target verschwindet dann.

Dokumentation

Dokumentation erstellen

Mit dem Unterbefehl cargo doc steht ein Werkzeug zur Verfügung, welches die Dokumentation der Schnittstellen aus dem Quelltext extrahiert. Das erzeugte HTML besitzt Hyperlinks und eine bequeme Suchfunktion.

Zunächst wird eine neue Bibliothek lib0 erstellt:

> cargo new --lib lib0

Alles das gehört zur öffentlichen Schnittstelle, was mit pub versehen wurde. Wir fügen in lib0/src/lib.rs eine öffentliche Funktion hinzu:

/// Die identische Funktion
pub fn id(x: i32) -> i32 {x}

Der Aufruf

> cargo doc --open

erstellt nun die Dokumentation als HTML. Die Option --open öffnet die Dokumentation danach sogleich im Browser. Die Signatur der Funktion und ihre Dokumentations-Kommentierung sind dort einsehbar.

Die Dokumentation der Standardbibliothek ist übrigens mit

> rustup doc --std

auch offline abrufbar.

Dokumentation schreiben

Zur Beschreibung einer Schnittstelle stellt man dieser einen Dokumentations-Kommentar voran. Im Gegensatz zu gewöhnlichen Kommentaren wird eine Zeile Dokumentations-Kommentierung mit drei anstelle zwei Schrägstrichen eingeleitet.

/// Kurzbeschreibung im ersten Absatz.
///
/// Ausführliche Beschreibung in den nachfolgenden
/// Absätzen.
pub fn id(x: i32) -> i32 {x}

Bei längeren Passagen mag das Einfügen der Schrägstriche ein wenig mühsam sein, etwa wenn man die Zeilen neu umbrechen will. Analog zu den gewöhnlichen Kommentaren gibt es daher bei Dokumentations-Kommentaren ebenfalls die Möglichkeit der Klammerung zur Umfassung mehrerer Zeilen.

/**
Kurzbeschreibung im ersten Absatz.

Ausführliche Beschreibung in den nachfolgenden
Absätzen.
*/
pub fn id(x: i32) -> i32 {x}

Die Zeichenanordnung in der Dokumentation ist nicht gänzlich beliebig, sondern wird als Markdown interpretiert. Herbei sind ein paar Kleinigkeiten zu beachten. So sollte man Programmterme und mathematische Ausdrücke mit Backticks (Gravis-Zeichen) umklammern. Außerdem ist die Syntax für Zeilenumbrüche in einem gewöhnlichen Texteditor unsichtbar.

  1. »The Cargo Book«. Offizielle Dokumentation.
  2. »The rustc book«. Offizielle Dokumentation.
  3. Manpage zu rustc.