© JrCasas/shutterstock.com
Was Rust in Sachen Speicherverwaltung so besonders macht

Rust Memory Ownership


Neue Programmiersprachen laden dazu ein, sie aus Neugier und Abenteuerlust auszuprobieren und auf ihre Alltagstauglichkeit zu prüfen. So ist Go für mich interessant, weil es sich perfekt für Containertechnologien eignet. Dann kam mit WebAssembly eine Plattform zum sicheren Ausführen von plattformunabhängigem Code aus dem Web, die meine Neugier weckte, und in deren Folge Rust. So wie Go die Sprache für Containertechnologien ist, so ist Rust die Sprache rund um WebAssembly. Eine der Eigenschaften von Rust ist das ausgefeilte Konzept von Memory Ownership. In diesem Artikel wird dieses Konzept vorgestellt und zum besseren Verständnis mit C# verglichen.

Ich programmiere C# seit den ersten Technologie-Preview-Versionen. Viele Jahre habe ich mich auf diese Programmiersprache und das damit verbundene .NET Framework konzentriert. Die am Beginn gängigste Alternative, Java, hat auf mich nie eine allzu große Anziehungskraft ausgeübt. Ich fühlte mich wohl bei C#. Vor einigen Jahren, als .NET in die Jahre gekommen war und von .NET Core nur erste Konzepte existierten, begann ich, mich bei anderen Sprachen umzusehen. Erst probierte ich TypeScript mit Node.js und fand sofort Gefallen daran. Diese Kombination wurde meine zweite Heimat, wenn es um serverseitigen Code ging. Am Client entschied ich mich damals ganz für TypeScript und beschloss, bei Webtechnologien zu bleiben. Mein zweites Programmiersprachenabenteuer begann vor ca. zwei Jahren. Go hatte mein Interesse geweckt und es macht mir mittlerweile großen Spaß, die Sprache Go in Cloud-Projekten einzusetzen. Besonders interessant ist Go für mich, da ich seit langem ein begeisterter Anhänger von Containertechnologien bin und sich Go dafür perfekt eignet.

Vor einigen Monaten bin ich dann auf eine technologische Entwicklung aufmerksam geworden, die das Potenzial hat, meine Gewohnheiten in Sachen Softwarearchitektur gehörig durcheinanderzuwirbeln: WebAssembly wird immer interessanter und darf aus meiner Sicht nicht rein als Plattform für Browseranwendungen missverstanden werden. WebAssembly ist eine Plattform zum sicheren Ausführen von plattformunabhängigem Code aus dem Web. Ob die Laufzeitumgebung im Browser, auf einem Server oder auf einem IoT-Gerät ausgeführt wird, ist dabei nebensächlich. WebAssembly wird meiner Einschätzung nach auf lange Sicht eine Alternative oder zumindest bedeutsame Ergänzung zur heute in der Praxis eingesetzten Containertechnologie.

So wie Go DIE Sprache ist, wenn es um Containertechnologien geht, so ist Rust DIE Sprache rund um WebAssembly. Das hat mein Interesse geweckt.

Zunächst wollte ich verstehen, was das Besondere an Rust ist, das neben den vielen schon existierenden Programmiersprachen eine weitere rechtfertigt. Eine der wichtigsten, charakteristischen Eigenschaften von Rust ist das ausgefeilte Konzept von Memory Ownership. In diesem Artikel möchte ich dieses Konzept vorstellen und zum besseren Verständnis mit C# vergleichen.

Das Problem

Vor gut einem Jahr hat Microsoft in einer Präsentation auf der BlueHat-Cybersecurity-Konferenz [1] eine interessante Grafik gezeigt (Abb. 1). Sie zeigt das Ergebnis einer Untersuchung dazu, wie viele CVEs (Common Vulnerabilities and Exposures) im Lauf der Jahre in Verbindung mit der Sicherheit der Speicherverwaltung standen. Microsoft kam auf eine erschreckend hohe Zahl von rund 70 Prozent, und was noch erstaunlicher ist: Diese Zahl bleibt seit Langem konstant. Trotz der enormen Anstrengungen, die Softwareschwergewichte wie Microsoft in Sachen Sicherheit des Quellcodes unternehmen, war und ist Speicherverwaltung ein großes Problem. Man sieht, dass wir uns als Programmiererinnen und Programmierer noch so sehr anstrengen können, wir kriegen es nicht hin. Wir brauchen bessere Werkzeugunterstützung, um Fortschritte zur erzielen.

stropek_rustmo_1.tif_fmt1.jpgAbb. 1: Common Vulnerabilities and Exposures (CVEs) durch Speicherverwaltung [1]

Sehen wir uns ein paar typische Beispiele von Speicherproblemen in C und C# an. Natürlich würde niemand heute noch solchen C-Code schreiben. Man würde fertige Klassen wie vector oder string verwenden. Man würde generell auf RAII (Resource acquisition is initialization) setzen, um Fehler zu vermeiden. Meine C-Beispiele verzichten darauf, da sie nicht den bestmöglichen C-Code zeigen, sondern Problembewusstsein schaffen sollen.

Listing 1 zeigt ein kurzes Beispielprogramm mit Memory Leak. getData() allokiert Speicher, den getAndProcessData() zur Weiterverarbeitung erhält. Dieser Speicher wird aber von niemandem freigegeben, wir haben ein Memory Leak.

Listing 1: Memory Leak

char *getData() {  char *buffer = (char*)malloc(1024); strcpy(buffer, "Hi!\n"); // read data from somewhere return buffer; } void getAndProcessData() {  char *buffer = getData(); cout << buffer; } int main() { getAndProcessData(); }

Listing 2 zeigt eine weitere, häufige Fehlerquelle: Use after Free. Der Speicher, auf den stmt zeigt, wird in einem gewissen Fall freigegeben, auf ihn wird später aber noch zugegriffen.

Listing 2: Use After Free

bool execSql(const char *stmt) { ... } void logError(const char *category, const char *d...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang