↑Programmieren in Rust
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.
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.
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.
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.
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 cleanaus. Das Verzeichnis
target
verschwindet dann.
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.
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.