Mutable Referenzen lösen alle Nebenläufigkeitsprobleme
Mutable Referenzen in Rust garantieren, dass sie die einzige zu diesem Moment existierende Referenz (immutabel oder mutabel) auf diese Datei sind - programmweit! Umgekehrt heißt das, dass niemand unsere Änderung "beobachten" kann. Wir brauchen uns hier also zum Beispiel über Nebenläufigkeit keine Gedanken zu machen. Wir wissen bereits aus der Funktionssignatur - die Funktion wird niemals nebenläufig auf dieselben Daten aufgerufen -, dass wir dazu zwei dieser Referenzen bräuchten. Damit muss unsere Funktionsimplementierung hier keine weiteren Vorkehrungen treffen.
Es gibt eine Menge Arten, mit dieser Signatur Fehler herbeizuführen. Die Eleganz liegt allerdings woanders: Es ist recht schwer, mutable Referenzen überhaupt zu produzieren. Das liegt vor allem an der Eigenschaft, dass sie eben programmweit - zur Kompilierzeit garantiert - einzigartig sind. Wenn wir also die obige Signatur sehen, können wir die Außenwelt (inklusive aller Dinge wie Threads, Nebenläufigkeit und so weiter) vergessen: Sie wären nur problematisch, wenn wir die Daten teilen würden.
Wir haben in diesem Fall also statisch garantierte Freiheit von Nebenläufigkeit. Auch hier: Wir haben dafür gar nicht arbeiten müssen - aber das Thema Nebenläufigkeit früh mitgedacht.
Um es nochmal hervorzuheben: Sowohl der Besitz als auch immutable und mutable Referenzen sind Grundbestandteile der Sprache und die erwähnten Regeln werden durch den Compiler durchgesetzt. Sie erzeugen dadurch auch keine Laufzeitkosten.
Werfen wir als Letztes einen Blick auf den Rückgabewert. Bei aller Sicherheit können bei IO-Vorgängen Fehler auftreten: Das Volume kann voll sein oder Ähnliches. Das wird auch hier durch den Result-Typ dargestellt.
fn write_to_file(file: &mut File, string_buffer: &str) -> Result<usize, io::Error> { // Implementierung ausgelassen }
Interessant ist hier, was uns das Resultat mitteilt: einmal eine Zahl (die Zahl der geschriebenen Bytes) und eventuell auftretende Fehler. Das können wir in allen der besprochenen Szenarien machen, ohne Abhängigkeiten zu den Parametern.
Rusts Geheimnis: Rust betrachtet Systeme
Sehen wir uns das gerade Gelernte an, fällt eine Sache auf: Wir haben uns gar nicht damit beschäftigt, was die Funktion eigentlich intern tut. Trotzdem haben wir mehrere verschiedene Varianten kennengelernt, die alle unterschiedliche Aussagen über die Umwelt treffen.
Die Ownership-Variante hat keine Abhängigkeiten zur Außenwelt, fordert dafür aber ein, dass die aufrufende Funktion Zugriff auf alle Daten verliert. Die zweite Variante fordert dies nur für das File-Handle, erwartet jedoch, dass die aufrufende Funktion die Daten im Speicher hält. Die dritte Variante verlangt weitreichende Garantien: Nicht nur müssen die Referenzen valide bleiben, eine davon muss exklusiv sein.
Funktionssignaturen drücken somit auch die Art der Kopplung zwischen Systemkomponenten aus - und das nicht nur im abstrakten Sinne, sondern ganz konkret durch Sprachkonstrukte. So wäre die erste Funktion eine sehr gute Variante, wenn wir die Aufgabe des Schreibens einfach an ein IO-System abgeben und dann vergessen wollen.
Würden wir hier referenzieren, würden wir plötzlich unsere Applikation an dieses System stärker koppeln. Die zweite und dritte Variante koppeln die aufrufende und die aufgerufene Funktion stärker - sie sollten sich also in derselben Komponente befinden. Insbesondere die dritte Variante verhindert effektive Nebenläufigkeitsfehler, indem sie für die Zeit des Funktionsaufrufs Nebenläufigkeit unterbindet.
Das mag auf den ersten Blick kleinlich erscheinen, liefert aber auf mittlere bis lange Sicht große Vorteile: Rust kann ausdrücken, welche Komponente gerade exklusiven Zugriff auf Speicher oder generell Ressourcen hat, und damit zum Beispiel gefährliche Refactorings unterbinden, die sonst häufig zu Fehlern führen können.
Zusammenfassung
Rust ist mehr als nur eine moderne, speichersichere Systemprogrammiersprache. Das fundamental in der Sprache verankerte und rigorose Ownership-Konzept ist nicht nur für die sichere Speicherverwaltung (ohne Garbage-Collector) geeignet, sondern ermöglicht auch Aussagen, die in "höheren" Sprache wie zum Beispiel Java schwer möglich sind.
Rust ist hier sogar nah an unserem Sprachgebrauch. In der Software-Architektur sprechen wir häufig davon, dass eine Komponente Verantwortung an eine andere "abgibt" - Rust gießt das in ein Sprachkonzept. Und die Frage "Wer besitzt diese Daten gerade?" ist eine, die gerade in nebenläufigen Systemen fundamental ist. Die Unterscheidung in mutable und immutable Zugriffsklassen gibt weitere zusätzliche Sichtbarkeit. All dies geschieht auf Sprachebene und nicht zur Laufzeit.
Um zurück zu Rusts Claim zu kommen: Zuverlässigkeit ist eine Eigenschaft eines Gesamtsystems, weshalb Rust einen starken Fokus darauf legt, wie Komponenten (jeder Größe) zusammenspielen, und es bietet Sprachkonstrukte, um hier neue und bessere Aussagen zu treffen. Das mag in der Konstruktion von Programmen gerade anfangs zu einer gefühlten Verlangsamung führen, sorgt aber auf lange Sicht für eine stabilere Entwicklungskurve.
Florian Gilcher ist seit 2013 im Rust-Bereich unterwegs und trainiert seit 2015 die Sprache professionell. Er hat die Berliner Usergruppe und das RustFest mitgegründet. Seit 2015 ist er auch Teil des Rust-Projekts, momentan als Mitglied des Rust Core Team und Direktor im Vorstand der Rust Foundation. Er ist einer der Gründer und Geschäftsführer von Ferrous Systems, die sowohl Trainings- als auch Implementierungsdienstleistungen rund um Rust anbietet. Auch für die Golem Akademie gibt er einen Rust-Einführungskurs.
Oder nutzen Sie das Golem-pur-Angebot
und lesen Golem.de
- ohne Werbung
- mit ausgeschaltetem Javascript
- mit RSS-Volltext-Feed
Rust macht es unmöglich, in eine geschlossene Datei zu schreiben |
oh wow, das ist krass, sehe gerade das nutzt https://crates.io/crates/druid
Jo ich meinte Rxjava und konsorten wobei ich für Vert.x schwärme :-) und eine Combo aus...
Was meinst du mit "DB Anwendung"? Gerade wenn man eine ordentliche Datenbank im...
So gemein das jetzt klingt - für die, die die Vorteile von solchen Sprachen nicht sehen...
Oh keine GUIs, alles singlethreaded, kein asynchroner I/O, keine Buffer? Über welche...
Kommentieren