Rust

Lesezeit: 7 Minuten

Rust ist eine kompilierte, statisch typisierte, prozedurale und funktionale Programmiersprache, die auch Teilaspekte objektorientierter Programmierung wie Polymorphie abbildet. Die Sprache entstand um 2006 aus einem persönlichen Projekt des Mozilla-Mitarbeiters Graydon Hoare, wird seit 2009 offiziell von Mozilla gesponsort und wurde 2010 erstmalig öffentlich bekannt gemacht. Am 15. Mai 2015 erschien die erste stabile Version von Rust.

Alleinstellungsmerkmale

Rusts Unique Selling Point ist eindeutig der Borrow Checker und damit verknüpft das Konzept Ownership. Diese beiden Begriffe beschreiben die Speicherverwaltung in Rust, die eine der größten Einstiegshürden beim Erlernen der Sprache darstellt. Rust schafft Speichersicherheit ohne eine Laufzeitumgebung mit automatischer Speicherbereinigung zu benötigen. Gleichzeitig ist es nicht erforderlich, wie in C manuell Speicher zu allokieren und zu deallokieren, womit Rust viele der in C typischen Fehler per Design eliminiert.

In Rust werden Variablen immer initialisiert, bevor sie verwendet werden. Null existiert in Rust als Wert nicht, stattdessen können Options verwendet werden, die entweder einen Wert beinhalten oder leer sind. Auf jeden Wert zeigt immer nur eine gültige Referenz. Ein Wert kann per Referenz an eine Funktion „ausgeliehen“ werden. Für kompliziertere Szenarien werden Variablen dabei mit Lifetimes annotiert. Diese Informationen verwendet der Borrow Checker zur Kompilierzeit, um zu gewährleisten, dass es immer nur eine gültige Referenz auf einen Wert gibt. In diesem Zuge werden auch die Anweisungen injiziert, die nicht mehr benötigten Speicher wieder freigeben. Der Entwickler sieht davon nichts, hat aber zur Laufzeit Gewissheit, dass nicht mehr verwendeter Speicher genau einmal freigegeben wird und es keine ungültigen Referenzen gibt.

Einsatzbereiche

Rust wird vor allem für systemnahe Software und auch im Bereich Embedded eingesetzt. Bei Mozilla wird Rust im Quantum-Projekt und für die neue Browser-Engine Servo verwendet. Die JavaScript-Laufzeitumgebung Deno ist zum Teil in Rust geschrieben, ebenso die Software Figma. Der Mikrokernel Redox und Teile des Betriebssystems Google Fuchsia sind in Rust geschrieben, genau wie Teile der Backends von Discord und OpenDNS. Microsoft Azure IoT Edge setzt für Teile seiner Plattform Rust ein, Amazon Web Services für einige Projekte wie Firecracker und Bottlerocket.

Die offizielle Webseite von Rust listet als Anwender viele große Unternehmen und Projekte auf, darunter 1Password, Atlassian, Bytedance, Canonical, Chef, Cloudflare, Coursera, Dropbox, npm, OVH, System76, Threema und Wire.

Pros

Rust ist sehr performant, bietet Speichersicherheit ohne Laufzeitumgebung, native Unterstützung für Nebenläufigkeiten und kommt mit vielen modernen Sprachkonstrukten. Es gibt Generics, Traits, Operator Overloading, Pattern Matching, Lambdas, Monaden, die auch zur Fehlerbehandlung verwendet werden, und eine meines Empfindens allgemein sehr angenehme, moderne Syntax mit Fluent Interfaces und Zero Cost Abstractions.

Das folgende Beispiel zeigt, wie eine Fließkommazahl aus dem String-Rückgabewert einer fiktiven Funktion getString() ausgelesen wird. Die verwendete Standardfunktion parse() gibt ein Result zurück, welches die Ausprägungen Ok() oder Err() haben kann. Als Fehlerbehandlung wird hier ein Standardwert von 0.00 verwendet. Die Variable amount wird hier zweimal deklariert, wobei nach Ausführung dieses Codes das Result amount nicht mehr zugänglich ist, nur noch die Fließkommazahl amount.

let amount = getString().parse::<f64>();
let amount = match amount {
    Ok(amount) => amount,
    Err(_error) => 0.00
};

Funktionen in Rust sind per Default private und Variablen immutable. Die Syntax ist an C angelehnt und die Schlüsselwörter sind allgemein sehr prägnant, etwa fn für function, mod für module, let für Variablen, pub und mut als Modifikatoren public und mutable. Rust kann Typen inferenzieren.

Rust unterstützt unter anderem keine variablen Argumentlisten (varargs), bietet aber die Möglichkeit, mit Makros die Sprache selbst zu erweitern. So kann Rust selbst keine String Interpolation, ermöglicht aber mit dem Makro format!(), Ähnliches zu erreichen:

let created_at: String = row.get(0).unwrap();
let amount: f32 = row.get(1).unwrap();
let description: String = row.get(2).unwrap();
rows.push(format!("\t{}\t{:.2}\t{}", created_at, amount, description));

Im Beispiel oben werden drei Variablen, von denen zwei Strings und eine eine Fließkommazahl beinhalten, per format!() durch Tabs getrennt in einen String geschrieben, wobei die Fließkommazahl auf zwei Nachkommastellen begrenzt wird.

Der Rust-Compiler liefert bei Kompilierfehlern sehr hilfreiche Hinweise und markiert präzise, wo sich in einer Zeile Code der Fehler versteckt, oftmals noch mit einem Lösungsvorschlag und einer allgemeinen Fehlerreferenz. Gerade am Anfang, wenn man sich noch nicht an das Ownership-Konzept gewöhnt hat, empfinde ich diese Hinweise als sehr hilfreich.

Mit dem Foreign Function Interface (FFI) bietet Rust die Möglichkeit, mit C-Code zu interagieren. Unter anderem in solchen Fällen mag es vorkommen, dass man die Sicherheit von Rust bewusst aushebeln möchte. Für diesen Zweck existiert Unsafe Rust mit fünf sogenannten Unsafe Superpowers, die Aktionen ermöglichen, die Rust ansonsten nicht zulassen würde.

Rust ist eine noch verhältnismäßig junge Sprache, bietet dafür aber bereits ein sehr großes Ökosystem. Der Rust-Compiler wird mit dem Package Manager Cargo ausgeliefert, der eine sehr einfache Abhängigkeitenverwaltung erlaubt. Aktuell ermöglicht Cargo die Nutzung von über 85000 Bibliotheken. Daneben gibt es Crater zur Prüfung, welche Bibliotheken mit welcher Rust-Version kompatibel sind, clippy als Linter, rustfmt als Formatter und rustdoc, um Dokumentation zu generieren.

Cons

Rust ist kompliziert und schwer zu erlernen, auch weil das Ownership-Konzept ein Novum ist und sich bisher nicht in anderen Programmiersprachen wiederfindet. Auch wenn man den Einstieg geschafft hat, so ist die Entwicklung langsamer als in sehr dynamischen Sprachen wie Python, womit Rust für Prototyping eher ungeeignet ist.

Ich finde es super, dass Rust mit einem Package Manager kommt, finde es aber nachteilig, dass dieser quasi alternativlos ist. Denn es ist sehr aufwändig bis unmöglich, Bibliotheken wie ein SQLite-Binding, die über Cargo installiert werden können, ohne diesen Paketmanager, also nur mit rustc, zu kompilieren. Auch ist es momentan erforderlich, sich mit einem GitHub-Account anzumelden, wenn man selbst Bibliotheken für Cargo veröffentlichen möchte. Damit muss man aber auch den AGB von GitHub zustimmen, was aus meiner Sicht nicht notwendig sein sollte, wenn man Open-Source-Bibliotheken für eine nicht zu GitHub gehörende Programmiersprache veröffentlichen will.

Datenblatt

NameRust
Webseitehttps://www.rust-lang.org/
Erscheinungsjahr2010
Aktuellste Version (Stand 17. Juli 2022)Version 1.62.0 vom 30. Juni 2022
Typisierungstatisch
Paradigmenimperativ
prozedural
objektorientiert
funktional
aspektorientiert
ProsCons
sehr schnellschwer zu erlernen
Speichersicherheit ohne Laufzeitumgebung; sehr hohe Fehlersicherheit; kein nullkompliziert
Generics, Traits, Operator Overloading, Pattern Matching, Lambdas, Monaden, Makros, Fluent Interfaces, Type Inference, viele Zero Cost Abstractions…ungeeignet für Prototyping / Rapid Application Development
private und immutable als DefaultsCargo ist quasi alternativlos
gute Compiler-Hinweise
Aufruf von C-Code
unbegrenzte Flexibilität durch Unsafe Rust
gutes Ökosystem
Cargo als Package Manager

virtuallet

virtuallet ist ein kleines Programm von mir, welches ich in diversen Programmiersprachen implementiert habe. Hier geht es direkt zum Rust-Code von virtuallet auf GitLab. Hier gibt es weitere Infos zu virtuallet.

- Graydon zur Wahl des Namens "Rust"
- Offizielle Dokumentation von Unsafe Rust