Schweitzer Fachinformationen
Wenn es um professionelles Wissen geht, ist Schweitzer Fachinformationen wegweisend. Kunden aus Recht und Beratung sowie Unternehmen, öffentliche Verwaltungen und Bibliotheken erhalten komplette Lösungen zum Beschaffen, Verwalten und Nutzen von digitalen und gedruckten Medien.
In diesem Kapitel werfen wir einen ersten Blick auf Rust-Programme, betrachten die Installation von Rust und der Sprachunterstützung in verschiedenen Entwicklungsumgebungen, sodass wir möglichst schnell praktische Schritte mit der Sprache unternehmen, ein Beispielprogramm schreiben und mit dem Rust-eigenen Build-System übersetzen und starten können.
Rust ist eine moderne Sprache, die sehr stark auf Geschwindigkeit und Parallelverarbeitung ausgelegt ist. Vielfach wird Rust als Systemprogrammiersprache und Ersatz für C dargestellt, der Anwendungsbereich ist aber sehr viel breiter. Betrachten wir ein paar der interessanten Eigenschaften von Rust.
Das absolute Alleinstellungsmerkmal ist die Art, wie Rust mit Speicher umgeht. Rust kann garantieren, dass durch die Verwaltung des Speichers zur Übersetzungszeit keine Fehler zur Laufzeit auftreten können. Damit braucht Rust auch keinen Garbage Collector. Das verhindert unbeabsichtigte Unterbrechungen im Programmablauf, um den Speicher aufzuräumen. Wir haben also nicht nur korrektere Programme, die schneller laufen, sie verhalten sich auch deterministischer.
Um dies zu erreichen, wird für jeden Wert ein Eigentümer festgelegt. Dies kann ein primitiver Wert sein oder eine beliebig komplexe Struktur. Ein Wert lebt, solange der Eigentümer lebt.
Der Eigentümer kann wechseln, und für den Zugriff auf ein Objekt können Referenzen ausgeliehen werden (Borrowing). Ausgeliehene Referenzen sind im Normalfall Lesereferenzen, es kann aber alternativ auch maximal eine Schreib-/Lese-Referenz auf einen Wert definiert werden. Dies impliziert, dass wir keine aktive Lesereferenz haben. Die Beschränkung auf eine einzige schreibende Instanz sorgt bei Neulingen meist für Überraschungen, hat aber den großen Vorteil, dass es keine undefinierten Zustände durch gleichzeitiges Schreiben oder nicht synchronisiertes Lesen geben kann.
Dieses Ownership genannte Konzept ist extrem mächtig, braucht aber zum vollständigen Verinnerlichen etwas Zeit und Übung. Wir werden dies in Abschnitt 7.2 kennenlernen und in Kapitel 15 im Detail beleuchten.
Rust ist eine Programmiersprache, die mit der Kapselung von Daten und Funktionen und Methoden auf diesen Daten objektorientierte Konzepte unterstützt.
Rust erreicht dies durch die Einführung von Modulen, die private und öffentliche Daten und Funktionen enthalten. Polymorphismus wird durch das Konzept der Traits erreicht, die inzwischen in vielen anderen Programmiersprachen wie Kotlin oder Scala auch verwendet werden. Eine vergleichbare Funktionalität gibt es in Java seit der Version 8 mit den Default-Methoden in Interface-Spezifikationen.
Rust bietet allerdings anders als die gewohnten objektorientierten Sprachen keine Vererbung. Dies mag im ersten Moment überraschen und ist eine Abkehr vom normalen objektorientierten Denken, hat aber gute Gründe.
Aus konzeptioneller Sicht ist es problematisch, dass wir bei der Vererbung nicht kontrollieren können, welche Teile unserer Elternklasse wir erben möchten. Dies kann dazu führen, dass wir in abgeleiteten Klassen Funktionalität haben, die dort nicht gewollt ist.
Das praktischere Argument ist aber, dass durch Verzicht auf Vererbung ein hoher Aufwand zur Identifikation der richtigen auszuführenden Methode/Funktion wegfällt. Dies macht Rust-Programme deutlich laufzeiteffizienter.
Wir werden uns mit objektorientierten Konzepten in Kapitel 9 auseinandersetzen.
Zur Unterstützung funktionaler Programmierung bietet Rust Closures, anonyme Funktionen, die auf ihre Umgebung zur Zeit der Definition zugreifen können. Dieses vielseitige Konstrukt findet sich in mehr und mehr Sprachen und erlaubt eine sehr elegante Kapselung von Funktionalität und Daten.
Zusammen mit Iteratoren, die die Verarbeitung von Sammlungen von Daten kapseln, erlauben Closures sehr mächtige funktionale Abstraktionen. Iteratoren und Closures werden wir in Kapitel 11 kennenlernen.
Rust bietet eine direkte Abstraktion der Thread-Funktionalität des unterliegenden Betriebssystems. Dies sorgt für den geringstmöglichen Mehraufwand zur Laufzeit, beschränkt aber natürlich die Flexibilität in der Verwendung von Threads auf die Unterstützung durch das unterliegende System. Bei Bedarf können allerdings auch Thread-Module verwendet werden, die eine unabhängige und damit flexiblere Implementierung anbieten. Dies erlaubt uns, von Fall zu Fall zu entscheiden, ob wir die größere Flexibilität oder den geringeren Speicherbedarf bevorzugen. Während die Entscheidung in vielen Fällen in Richtung der Flexibilität getroffen werden wird, gibt es eingeschränkte Umgebungen (wie zum Beispiel Mikro-Controller), in denen die Möglichkeit der expliziten Wahl sehr vorteilhaft ist.
Viele der Probleme, die bei der normalen Programmierung von paralleler Verarbeitung zu sehr hoher Komplexität und damit zu schwer auffindbaren Fehlern führen, finden wir in Rust nicht. Dies entsteht durch das Ownership-Modell, das dafür sorgt, dass der Compiler problematische Stellen im Quelltext sehr früh identifizieren und damit entfernen kann. Das heißt nicht, dass Rust alle Probleme im Zusammenhang mit Parallelprogrammierung löst. Es erlaubt uns aber, uns auf die wirklich schwierigen Probleme zu konzentrieren.
Threads in Rust können kommunizieren, indem sie Nachrichten in verschiedene Kanälen senden oder aus diesen empfangen. Zusätzlich können sie Teile ihres Zustands geschützt durch eine Mutex-Abstraktion mit anderen Threads teilen.
Parallelprogrammierung in Rust ist sehr mächtig, und wir werden uns in Kapitel 16 eingehend damit beschäftigen.
Als ein erstes Beispiel, um Ihren Appetit für Rust zu wecken, betrachten wir ein kleines Programm, das bereits viele Eigenschaften von Rust zeigt. Da dieses deutlich über das klassische »Hallo Welt«-Programm hinausgeht, sollten Sie sich keine Sorgen machen, wenn einzelne Funktionalitäten noch nicht vollständig klar sind. Die Erklärungen sind an dieser Stelle notwendigerweise etwas kurz, alle angesprochenen Eigenschaften werden wir später deutlich detaillierter betrachten.
Listing 1-1Zeilenweises Lesen und Ausgabe einer Datei
use std::fs::File as Datei;
use std::io::{BufReader, BufRead};
fn main() {
let file = Datei::open("hallo.txt")
.expect("Konnte Datei nicht öffnen");
let reader = BufReader::new(file);
for line in reader.lines() {
let line = line
.expect("Konnte Zeile nicht lesen");
println!("{}", line);
}
Wir beginnen mit dem Import benötigter Funktionalität (wie auch aus Java bekannt). Wir benennen den aus dem Namensraum beziehungsweise Modul std::fs (durch den Pfadtrenner :: getrennte Namen sind hierarchische Pfadangaben) importierten Typ File um in Datei und importieren im nächsten Schritt die beiden Typen BufReader und BufRead. Der Typ BufReader unterstützt gepuffertes Lesen aus einer Quelle und ist damit deutlich effizienter als ein direktes Lesen.
Dann folgt die Definition unserer ersten Funktion, gekennzeichnet durch das Schlüsselwort fn. In unserem Fall ist dies die Funktion main(), die keine Parameter und keinen Rückgabewert hat. Der Körper der Funktion findet sich im durch geschweifte Klammern definierten Block von Anweisungen, die durch Semikolon getrennt sind. Wie in vielen anderen Sprachen hat diese Funktion eine Sonderrolle: Sie ist der Einstiegspunkt in unser Programm und wird als Erstes aufgerufen, um den Programmabfluss zu starten.
Wir versuchen mit der Methode open() (eine Methode des Typs Datei, unserem umbenannten Typ File) eine Datei mit dem Namen hallo.txt zu öffnen. Dieser Aufruf liefert ein Objekt vom Typ Result zurück, das entweder das Ergebnis des erfolgreichen Aufrufs oder den durch den Aufruf ausgelösten Fehler enthält. Die Funktion expect() nimmt dieses Objekt und liefert im Erfolgsfall das Ergebnis zurück, im Fehlerfall wird die als Parameter übergebene Nachricht ausgegeben und das Programm mit einem...
Dateiformat: ePUBKopierschutz: Wasserzeichen-DRM (Digital Rights Management)
Systemvoraussetzungen:
Das Dateiformat ePUB ist sehr gut für Romane und Sachbücher geeignet - also für „fließenden” Text ohne komplexes Layout. Bei E-Readern oder Smartphones passt sich der Zeilen- und Seitenumbruch automatisch den kleinen Displays an. Mit Wasserzeichen-DRM wird hier ein „weicher” Kopierschutz verwendet. Daher ist technisch zwar alles möglich – sogar eine unzulässige Weitergabe. Aber an sichtbaren und unsichtbaren Stellen wird der Käufer des E-Books als Wasserzeichen hinterlegt, sodass im Falle eines Missbrauchs die Spur zurückverfolgt werden kann.
Weitere Informationen finden Sie in unserer E-Book Hilfe.