
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
Name | Rust |
Webseite | https://www.rust-lang.org/ |
Erscheinungsjahr | 2010 |
Aktuellste Version (Stand 17. Juli 2022) | Version 1.62.0 vom 30. Juni 2022 |
Typisierung | statisch |
Paradigmen | imperativ prozedural objektorientiert funktional aspektorientiert |
Pros | Cons |
---|---|
sehr schnell | schwer zu erlernen |
Speichersicherheit ohne Laufzeitumgebung; sehr hohe Fehlersicherheit; kein null | kompliziert |
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 Defaults | Cargo 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