13. November 2023 von Marc Mezger
Einführung in die Programmiersprache Rust
Was ist Rust?
Rust ist eine moderne, systemnahe Programmiersprache, die darauf ausgerichtet ist, hohe Leistung, Zuverlässigkeit und Produktivität zu bieten. Diese Programmiersprache entstand aus einem persönlichen Projekt des Mozilla-Mitarbeiters Graydon Hoare. Mozilla begann das Sponsoring des Projekts im Jahr 2009 und 2010 wurde es zum ersten Mal angekündigt. Im selben Jahr begann der Wechsel von einem ersten Compiler zu einem neuen Compiler, der selbst in Rust geschrieben war. Dieser rustc genannte Compiler verwendet LLVM als Backend und kann sich seit 2011 erfolgreich selbst übersetzen. Eine erste stabile Version von Compiler und Standardbibliothek, Rust 1.0, wurde am 15. Mai 2015 veröffentlicht.
Rust wurde entwickelt, um eine Sprache zu schaffen, die die Sicherheitslücken und Speicherfehler vermeidet, die in anderen systemnahen Sprachen wie C oder C++ häufig auftreten. Es bietet eine Vielzahl von Funktionen, die es zu einer attraktiven Wahl für viele verschiedene Arten von Softwareprojekten machen.
Die Anhängerinnen und Anhänger der Programmiersprache Rust bezeichnen sich selbst als „Rustaceans“, ein Wortspiel mit der englischen Bezeichnung für Krebstiere, „Crustaceans“. Diese Gemeinschaft von Rust-Enthusiastinnen und -Enthusiasten ist bekannt für ihre Unterstützung und Zusammenarbeit, um Rust zu einer sichereren und effizienteren Programmiersprache zu machen.
Was macht Rust so besonders?
Speichersicherheit ohne Garbage Collector
Rust bietet eine starke Speichersicherheit, ohne einen Garbage Collector zu verwenden. Dies wird durch das Konzept des „Ownership“ mit Regelwerken für Borrowing und Lifetimes erreicht. Dadurch kann Rust Speicherzugriffsfehler verhindern, ohne die Notwendigkeit einer Laufzeitüberprüfung, was die Leistung der Anwendung verbessert.
fn main() {
let s1 = String::from("hello");
let mut s2 = s1; // s1 ist hier nicht mehr gültig
s2.push_str(", world"); // s2 ist nun "hello, world"
println!("{}", s2);
// Wenn wir versuchen, s1 zu verwenden, wird der Compiler einen Fehler ausgeben
// println!("{}", s1); // Dieser Code verursacht einen Kompilierungsfehler
}
In diesem Code erstellen wir zuerst die Variable s1 mit dem Wert „hello“. Anschließend weisen wir s1 s2 zu. In Rust bedeutet dies, dass s1 an s2 „ausgeliehen“ wird und s1 ab diesem Punkt ungültig ist. Wenn wir versuchen, s1 nach dieser Zuweisung zu verwenden, gibt der Rust-Borrow-Checker einen Fehler aus, weil s1 nicht mehr gültig ist.
Der Borrow Checker in Rust sorgt dafür, dass Referenzen immer gültig sind. Er verhindert, dass Daten gelöscht oder geändert werden, während noch auf sie verwiesen wird. Dies schützt vor einer Vielzahl von Fehlern, die in anderen Sprachen häufig auftreten.
In einem etwas komplexeren Beispiel könnten wir den Borrow Checker verwenden, um sicherzustellen, dass wir eine Datenstruktur nicht verändern, während wir sie lesen:
fn main() {
let mut s = String::from("hello");
let r1 = &s; // Kein Problem
let r2 = &s; // Kein Problem
println!("{} and {}", r1, r2);
let r3 = &mut s; // Problem!
println!("{}", r3);
}
In diesem Code haben wir zwei unveränderliche Referenzen zu s (r1 und r2), und dann versuchen wir, eine veränderliche Referenz zu s zu erstellen (r3). Der Rust-Borrow-Checker wird diesen Code nicht kompilieren, weil er verhindert, dass wir s ändern, während r1 und r2 darauf verweisen. Dies ist ein weiteres Beispiel dafür, wie der Rust-Borrow-Checker dazu beiträgt, Race Conditions und andere Arten von Fehlern zu verhindern.
Null-Kosten-Abstraktion
Rust ist eine einzigartige Programmiersprache, die Entwicklerinnen und Entwicklern die Möglichkeit bietet, auf einem hohen Abstraktionsniveau zu arbeiten, ohne dass Leistungseinbußen in Kauf genommen werden müssen. Dieses Prinzip, bekannt als „Zero-Cost-Abstraction“, steht im Zentrum der Philosophie von Rust und ist ein entscheidender Faktor, der diese Sprache von vielen anderen unterscheidet. Die Idee der Zero-Cost-Abstraction ist einfach, aber mächtig. Sie bedeutet, dass abstrakte Konstrukte und höhere Programmierkonzepte, die in der Sprache eingebettet sind, verwendet werden können, ohne dass dies zu einer Verschlechterung der Ausführungsgeschwindigkeit oder Effizienz führt. Mit anderen Worten: Die Abstraktionen, die Rust zur Verfügung stellt, haben keine zusätzlichen Laufzeitkosten im Vergleich zu ihren handgeschriebenen, weniger abstrakten Äquivalenten. Mit Rust können Entwicklerinnen und Entwickler daher einen Code schreiben, der ebenso schnell und effizient ist wie ein optimierter Code auf niedriger Ebene, während sie gleichzeitig die Vorteile von hohen Abstraktionen nutzen, die das Programmieren einfacher und sicherer machen. Dies ermöglicht es Entwicklerinnen und Entwicklern, sich auf die Lösung des eigentlichen Problems zu konzentrieren, ohne sich Gedanken darüber machen zu müssen, wie ihre hohen Abstraktionen die Leistung beeinflussen könnten.
Ein weiterer Vorteil von Rusts Null-Kosten-Abstraktionen ist, dass sie es Entwicklerinnen und Entwicklern ermöglichen, präzise Kontrolle über Systemressourcen zu haben, ähnlich wie in Systemprogrammiersprachen auf niedriger Ebene. Dies macht Rust ideal für Anwendungen, bei denen die Leistung von entscheidender Bedeutung ist, wie beispielsweise bei Systemprogrammierung, eingebetteten Systemen und leistungskritischen Webanwendungen.
Die Fähigkeit, auf einem hohen Abstraktionsniveau zu arbeiten, ohne Leistungseinbußen hinnehmen zu müssen, macht Rust zu einer attraktiven Option für Entwicklerinnen und Entwickler, die die Kontrolle und Effizienz von systemnaher Programmierung mit der Einfachheit und Sicherheit von höheren Abstraktionen kombinieren möchten.
struct Point {
x: i32,
y: i32,
}
impl Point {
fn new(x: i32, y: i32) -> Point {
Point { x, y }
}
fn distance(&self, other: &Point) -> f64 {
let x_diff = other.x - self.x;
let y_diff = other.y - self.y;
((x_diff.pow(2) + y_diff.pow(2)) as f64).sqrt()
}
}
fn main() {
let p1 = Point::new(0, 0);
let p2 = Point::new(5, 3);
println!("Distance: {}", p1.distance(&p2));
}
In diesem Code definieren wir die Struktur Point mit den Feldern x und y. Wir implementieren eine Methode new zur Erstellung neuer Point-Instanzen und eine Methode distance zur Berechnung der Entfernung zwischen zwei Punkten.
Obwohl wir auf hohem Niveau mit Punkten und Methoden arbeiten, ist der resultierende Maschinencode genauso effizient, als wenn wir die Berechnungen manuell durchgeführt hätten. Dies ist ein Beispiel für eine Null-Kosten-Abstraktion: Wir können abstrakte, leicht zu verstehende Konstrukte verwenden, ohne dass die Leistung darunter leidet.
Dies ist besonders nützlich in großen und komplexen Codebasen, wo die Verwendung von Abstraktionen helfen kann, den Code sauberer und einfacher zu verstehen, ohne dass die Ausführungsgeschwindigkeit darunter leidet.
Konkurrierende Ausführung ohne Race Conditions
Rusts Typsystem und Eigentumskonzept helfen dabei, Race Conditions zu vermeiden. Dies macht Rust zu einer hervorragenden Wahl für Systeme, die concurrent oder parallel ausgeführt werden müssen, beispielsweise Betriebssysteme und Webserver.
Cargo – Packagemanager
Der integrierte Paketmanager von Rust, genannt Cargo, verbessert die Projektverwaltung, die Nachverfolgung von Abhängigkeiten und den Build-Prozess erheblich, wodurch zu einem optimierten und gut strukturierten Entwicklungsworkflow beigetragen wird. Eine interessante historische Anmerkung ist, dass Rust die Auszeichnung hat, die erste Systemprogrammiersprache zu sein, die einen standardisierten Paketmanager integriert hat. Diese wegweisende Maßnahme hat zur Entwicklung eines außergewöhnlich robusten und umfassenden Rust-Ökosystems geführt.
[workspace]
resolver = "2"
members = [
"crates/*",
"credential/*",
"benches/benchsuite",
"benches/capture",
]
exclude = [
"target/", # exclude bench testing
]
[workspace.package]
rust-version = "1.73" # MSRV:1
edition = "2021"
license = "MIT OR Apache-2.0"
[workspace.dependencies]
anstream = "0.6.4"
anstyle = "1.0.4"
anyhow = "1.0.75"
base64 = "0.21.5"
bytesize = "1.3"
cargo = { path = "" }
Effiziente C-Interoperabilität
Rust kann problemlos mit C interagieren und ermöglicht es Entwicklerinnen und Entwicklern, den bestehenden C-Code sicherer zu machen, ohne den gesamten Code neu schreiben zu müssen. Dies ist ein großer Vorteil für Systeme, die bereits in C geschrieben wurden.
Aktive Community und „meistgeliebte“ Sprache
Rust hat eine sehr aktive und wachsende Community. Seit mehreren Jahren in Folge wurde Rust in der Stack Overflow Developer Survey zur „meistgeliebten“ Programmiersprache gewählt. Die Community bietet viele Ressourcen und Libraries und die Dokumentation ist ausgezeichnet.
Diese Eigenschaften machen Rust zu einer sehr attraktiven Option für Entwicklerinnen und Entwickler, die eine sichere, effiziente und moderne Programmiersprache suchen.
Praktische Anwendbarkeit von Rust
Rust, bekannt für seine Fähigkeit, eine hohe Leistung zu liefern und gleichzeitig eine starke Speichersicherheit zu gewährleisten, hat sich im Laufe der Jahre als eine bemerkenswerte Wahl für eine Vielzahl von Anwendungsfällen erwiesen. Rusts Kombination aus Sicherheit, modernem Tooling und einer engagierten Community macht es zu einem attraktiven Werkzeug für viele praktische Anwendungen.
- Systemprogrammierung: Rusts Fokus auf Null-Kosten-Abstraktionen und direkte Hardware-Zugriffe macht es ideal für die Systemprogrammierung. Es ermöglicht Entwicklerinnen und Entwicklern das Schreiben von Betriebssystemen, Dateisystemen, Browsern und mehr, die effizient sind und gleichzeitig die Speichersicherheit gewährleisten, die solche Systeme erfordern.
- WebAssembly: Rust hat sich als eine hervorragende Wahl für die Arbeit mit WebAssembly (Wasm) etabliert, einem binären Format, das schnelle, sichere und portable Anwendungen im Web ermöglicht. Mit Rust und Wasm können Entwicklerinnen und Entwickler leistungsstarke Frontend-Anwendungen erstellen, die nahezu native Geschwindigkeit bieten.
- Embedded Systems: Rusts Kontrolle über Systemressourcen und seine Fähigkeit, ohne Garbage Collector zu arbeiten, machen es zu einer attraktiven Wahl für eingebettete Systeme. Entwicklerinnen und Entwickler können direkt auf die Hardware zugreifen und gleichzeitig von Rusts Sicherheitsgarantien profitieren.
- Webserver und Netzwerkdienste: Mit seiner starken Konkurrenzfähigkeit und Sicherheit ist Rust auch eine hervorragende Wahl für die Erstellung von Webservern und Netzwerkdiensten. Bibliotheken wie Tokio und Actix machen es einfach, hochleistungsfähige und gleichzeitig sichere Netzwerkanwendungen zu erstellen.
Wer verwendet Rust?
Rust wird von einer Vielzahl großer Unternehmen für ihre technologischen Anforderungen genutzt. Amazon Web Services, Amazons eigene Cloud-Sparte, nutzt Rust zur Entwicklung von Hochleistungs- und Sicherheitsinfrastrukturnetzwerken und anderer Systemsoftware. Microsoft Corporation, eines der Gründungsmitglieder der Rust Foundation, setzt Rust in ihrem Rust-for-Windows-Projekt ein. Dieses Projekt ermöglicht es, jede Windows API mithilfe von Rust verwenden zu können. Meta, ehemals bekannt als Facebook, hat Rust in verschiedenen Projekten eingesetzt, einschließlich in dem umstrittenen Libra-Kryptowährungs- und Blockchain-Projekt. Dropbox nutzte Rust als Teil eines größeren Projekts zur Verbesserung der Effizienz von Rechenzentren und es wurde verwendet, um mehrere Komponenten des Kern-Datei-Speichersystems von Dropbox zu schreiben. Mozilla Corporation, der erste Investor in Rust, hat Stylo, die CSS-Engine von Firefox, mit Rust gebaut. Cloudflare, ein Unternehmen, das sich auf Webinfrastruktur und Website-Sicherheit spezialisiert, setzt Rust ein, da es erstklassige WebAssembly-Unterstützung und ein florierendes Ökosystem bietet.
Auch andere Unternehmen wie Coursera, Discord, Figma und npm haben die Vorteile von Rust erkannt und nutzen es in ihrem Tech-Stack. Die weit verbreitete Anwendung von Rust in diesen führenden Unternehmen unterstreicht die Leistungsfähigkeit und Vielseitigkeit der Sprache in der praktischen Anwendung.
Ausblick
In den kommenden Beiträgen werde ich mich intensiv mit den Themen „Rewrite Everything in Rust“ und „Rust in Python“ auseinandersetzen. Diese Diskussionen zielen darauf ab, die vielseitige Anwendbarkeit von Rust zu demonstrieren und die Integration mit anderen weit verbreiteten Sprachen wie Python zu beleuchten.
Ich hoffe, diese kurze Einführung hat euer Interesse an der Erkundung der Programmiersprache Rust geweckt. Unabhängig von eurem derzeitigen Kenntnisstand, ob ihr erfahrene Entwicklerinnen und Entwickler oder Anfängerinnen und Anfänger seid, Rust bietet euch eine einzigartige Perspektive und eine lohnende Herausforderung. Ich lade euch herzlich ein, die Möglichkeiten von Rust zu entdecken, und freue mich darauf, euch auf dieser spannenden Reise zu begleiten.