Rust (lang): Sicherheit vs. Kontrolle

in #rust-lang7 years ago (edited)

Rust ist eine relative neue Programmiersprache, die sich seit 2010 in Entwicklung befinden, die erste stabile Version wurde 2015 veröffentlicht. Es handelt sich um eine Systemprogrammiersprache, das heißt man kann mit Rust Betriebssysteme und Mikrocontroller programmieren. Damit ist Rust ein Konkurrent zu den etablierten Systemprogrammiersprachen, wie C und C++.

Anders als höhere Programmiersprachen, wie Java oder Javascript, braucht eine Systemprogrammiersprache keine Laufzeitumgebung. Eine Laufzeitumgebung macht das Programmieren von Betriebssysteme und Mikrocontroller schwer bis unmöglich, außerdem kann sie zu langsamerer Ausführungsgeschwindigkeit und mehr Speicherverbrauch führen. Die Java Laufzeitumgebung braucht alleine schon etwa 60MB an Speicherplatz. Auf einem Mikrocontroller mit wenigen hundert KB an Speicher ist das undenkbar.

Sicherheit vs. Kontrolle

Rust eignet sich aber auch für die Entwicklung von normalen Anwendungen. Höhere Sprachen haben den Vorteil, dass sie die komplett Kontrolle über die Speicherverwaltung übernehmen. So lässt Java kein use after free, also dem Benutzen von bereits freigegebenem Speicher, zu. Das Problem bei use after free ist, dass man einen Speicherbereich verwendet, der bereits als “frei” markiert wurde und damit von dem System neu zugeordnet werden darf. Im besten Fall wird der Bereich nicht vom System neu vergeben und die Anwendung läuft wie gewollt. Wird der Speicher aber neu vergeben und verwendet, findet eine “Doppel-Nutzung” des Bereichs statt. Der zweite Nutzer legt dort seine Daten ab. Der erste Nutzer erwartet das nicht und das Programm stürzt ab. Das führt zu sehr schwer auffindbaren Fehlern, da diese nur “zufällig” auftreten.

Java verwendet einen Garbage Collector um dieses Problem zu umgehen. Der GC geht periodisch alle belegten Speicherbereiche durch und gibt die nicht mehr benötigten frei. Die Laufzeitumgebung, zu der auch der GC gehört, ist auch für die Zuweisung von neuen Blöcken verantwortlich, damit gibt man die Kontrolle über die Speicherverwaltung an die Laufzeitumgebung ab. Das ist aber bei der Programmierung von Betriebssystemen nicht gewünscht. Auch kostet der GC Performance und mehr Speicherplatz, da der Speicher nicht direkt dann freigegeben wird, wenn er nicht mehr gebraucht wird, sondern verzögert, nämlich beim GC-Lauf.

Bei C und C++ muss der Speicher manuell verwaltet werden, wenn falsch angewendet kann das zu Fehlern, wie use-after-free, führen.

int main() {
    int *pointer = malloc(sizeof(int)); // allokiere/hole Speicher für eine Zahl (int, Integer, ganze Zahl)
    // [...] benutze den Speicher
    free(pointer); // gib den Speicher wieder frei
    // [...] wenn man den Speicherbereich hier weiterhin verwendet, ist es ein use-after-free
}

Rust macht die Speicherverwaltung, wie der GC auch, automatisch. Jedoch, anders als beim GC, zur Kompilezeit, also beim Übersetzten des Quellcodes in Maschinencode und nicht zur Laufzeit.

fn main() {
    let mut meine_zahl = Box::new(0); // allokiere Speicher und weise 0 zu
    // [...] benutze den Speicher
    // Speicher wird hier wieder freigegeben, da der Kompiler weiß, dass er nicht mehr verwendet wird
}

Dabei erzeugt der Kompiler Maschinencode, der dem Code von C entspricht. Aber ohne die Gefahr, einen Fehler bei der manuellen Speicherverwaltung zu begehen. Sprich man bekommt die Sicherheit von Java, aber ohne die Kosten des GCs.

Wenn man Kontrolle über den Speicher will, kann man auf unsafe-Code zurückgreifen. unsafe-Code aktiviert die “Superkräfte” alles zu tun, was im sicheren Rust-Code nicht erlaubt wäre. Das in aber nur in den seltensten Fällen wirklich nötig. Die stdlib bietet allerhand Hilfsmittel, die intern unsafe verwenden, aber komplett sicher benutzt werden können, wie Box aus dem Code-Beispiel.

Rust vereint beides, die Sicherheit und optional aktivierbar, mit weniger Sicherheit, die Kontrolle.

Coin Marketplace

STEEM 0.20
TRX 0.16
JST 0.030
BTC 65910.66
ETH 2696.65
USDT 1.00
SBD 2.88