Marburger | Rust | E-Book | www.sack.de
E-Book

E-Book, Deutsch, 1016 Seiten

Reihe: Rheinwerk Computing

Marburger Rust

Das umfassende Handbuch
1. Auflage 2024
ISBN: 978-3-8362-9020-3
Verlag: Rheinwerk
Format: EPUB
Kopierschutz: 0 - No protection

Das umfassende Handbuch

E-Book, Deutsch, 1016 Seiten

Reihe: Rheinwerk Computing

ISBN: 978-3-8362-9020-3
Verlag: Rheinwerk
Format: EPUB
Kopierschutz: 0 - No protection



Werden Sie Rust-Profi und entwickeln Sie moderne, sichere Software!

Rust ist für sein cleveres Speichermanagement, hervorragende Sicherheit und viele beliebte Features bekannt. Nutzen Sie das praktische Design dieser Sprache, um moderne und robuste Software auf professionellem Niveau zu entwickeln! Dieses Handbuch begleitet Sie beim Einstieg und stellt Rust dann detailliert vor – sowohl für systemnahe Programmierung als auch für Enterprise- und Cloud-Anwendungen. Mit vielen gut kommentierten Codebeispielen , Best Practices und einer durchgehenden Demo-Anwendung . Zugleich lassen sich alle Kapitel gut einzeln lesen und zum Nachschlagen verwenden. Einsteiger in Rust, professionelle Anwender*innen und Sprachensammler kommen hier auf ihre Kosten. Der ideale Weg zu fundierten und umfassenden Kenntnissen in Rust!

Aus dem Inhalt:

  • Rust installieren, Toolchain aufsetzen
  • Fundierter Einstieg
  • Speichermanagement
  • Traits und Closures
  • Collections und Iteratoren
  • Asynchrone Programmierung
  • Multithreading
  • Arbeiten mit Text
  • Dokumentation und Test automatisieren
  • Schnittstellen zu anderen Sprachen
Marburger Rust jetzt bestellen!

Autoren/Hrsg.


Weitere Infos & Material


  Materialien zum Buch ... 18

  1.  Über dieses Buch ... 19


       1.1 ... Was Sie in diesem Buch lernen werden ... 20

       1.2 ... Was dieses Buch Ihnen zeigen möchte ... 21

       1.3 ... Noch mehr Informationen und Guides ... 22

       1.4 ... Danksagung ... 24

  2.  Die Installation, die IDE und »Hallo Rust« ... 25


       2.1 ... Wie Sie Rust installieren ... 25

       2.2 ... Eine Entwicklungsumgebung wählen ... 28

       2.3 ... Das erste Programm ... 30

       2.4 ... Wie es weitergeht ... 33

  3.  Variablen und Datentypen ... 35


       3.1 ... Prelude: Die Standardimporte ... 35

       3.2 ... Variablen ... 36

       3.3 ... Konstanten ... 56

       3.4 ... Skalare Datentypen ... 60

       3.5 ... Wie Rust mit »Option

       3.6 ... Zusammenfassung ... 84

  4.  Speichernutzung und Referenzen ... 87


       4.1 ... Wichtige Speicherbereiche ... 87

       4.2 ... Eigentumsverhältnisse im Speicher ... 89

       4.3 ... Referenzen und der leihweise Zugriff ... 98

       4.4 ... Mit Box Objekte im Heap ablegen ... 111

       4.5 ... Zusammenfassung ... 121

  5.  Strings ... 123


       5.1 ... Der String-Slice ... 123

       5.2 ... Der String ... 134

       5.3 ... Wie Sie Strings formatieren ... 147

       5.4 ... Zusammenfassung ... 154

  6.  Collections ... 157


       6.1 ... Tupel ... 157

       6.2 ... Arrays ... 166

       6.3 ... Elementbereiche ... 173

       6.4 ... Vektoren ... 182

       6.5 ... Slices ... 214

       6.6 ... HashMap und BTreeMap ... 231

       6.7 ... Hashes ... 245

       6.8 ... Mengen verwalten ... 248

       6.9 ... Die Entry API ... 251

       6.10 ... Elemente verschiedener Datentypen in eine Collection einfügen ... 257

       6.11 ... Zusammenfassung ... 260

  7.  Funktionen ... 263


       7.1 ... Der Aufbau einer Funktion ... 264

       7.2 ... Funktionszeiger ... 268

       7.3 ... Referenzen und Lebenszeiten in Funktionen ... 271

       7.4 ... Konstante Funktionen ... 280

       7.5 ... Anonyme Funktionen und Closures ... 285

       7.6 ... Funktions-Traits ... 302

       7.7 ... Zusammenfassung ... 311

  8.  Anweisungen, Ausdrücke und Muster ... 313


       8.1 ... Von der Anweisung zum Ausdruck und Muster ... 313

       8.2 ... Die Zuweisung im Detail ... 316

       8.3 ... Speicherausdrücke ... 319

       8.4 ... Operatoren ... 325

       8.5 ... Konditionale Ausdrücke ... 330

       8.6 ... Schleifen ... 342

       8.7 ... Muster ... 350

       8.8 ... Zusammenfassung ... 364

  9.  Fehlerbehandlung ... 367


       9.1 ... Fehler, von denen sich das Programm nicht erholen kann ... 367

       9.2 ... Erwartbare Fehler behandeln ... 381

       9.3 ... Zusammenfassung ... 418

10.  Strukturen ... 421


       10.1 ... Daten zusammenhängend ablegen ... 422

       10.2 ... Records: Der Struktur-Urtyp ... 423

       10.3 ... Strukturen und Instanzen ... 426

       10.4 ... Lebenszeiten: Wenn Felder Referenzen enthalten ... 441

       10.5 ... Wie Sie dem Compiler mit PhantomData wichtige Typinformationen übergeben ... 449

       10.6 ... Eine Datenstruktur ohne feste Größe ... 460

       10.7 ... Die drei Strukturen ... 462

       10.8 ... Muster ... 466

       10.9 ... Daten und Verhalten sind getrennt ... 468

       10.10 ... Strukturen in der Praxis: Das Bestellsystem überarbeiten ... 475

       10.11 ... Assoziierte Funktionen und die new-Konvention ... 480

       10.12 ... Methoden ... 486

       10.13 ... Referenzen in assoziierten Funktionen und Methoden ... 501

       10.14 ... Praxisbeispiel: Simulationsfähigkeiten im Restaurant-System ... 503

       10.15 ... Rekursion in Strukturen ... 507

       10.16 ... Typ-Aliasse ... 510

       10.17 ... Zusammenfassung ... 512

11.  Traits ... 515


       11.1 ... Marker-Traits ... 516

       11.2 ... Trait-Implementierungsblöcke ... 517

       11.3 ... Sie können ein Trait jeweils für T und &T implementieren ... 541

       11.4 ... Super-Traits ... 546

       11.5 ... Trait-Objekte ... 549

       11.6 ... Beispielprojekt: Trait-Objekte von »Form« ... 564

       11.7 ... Undurchsichtige Datentypen zurückgeben ... 574

       11.8 ... Traits in der Praxis ... 578

       11.9 ... Zusammenfassung ... 627

12.  Enumerationen ... 631


       12.1 ... Die Eigenschaften einer Enumeration ... 632

       12.2 ... Verschiedene Variant-Typen ... 644

       12.3 ... Enumerationen und Muster ... 656

       12.4 ... Implementierungsblöcke und Verhalten ... 660

       12.5 ... Zusammenfassung ... 667

13.  Module, Pfade und Crates ... 669


       13.1 ... Das Modul ... 669

       13.2 ... Pfade ... 697

       13.3 ... Vom Crate zum Paket, vom Paket zum Workspace ... 721

       13.4 ... Zusammenfassung ... 777

14.  Generische Programmierung ... 781


       14.1 ... Von der Vorlage zur Konkretisierung: Monomorphisierung ... 781

       14.2 ... Typparameter, generische Konstanten und Lebenszeiten ... 783

       14.3 ... Syntaktische Elemente, die generisch sein können ... 785

       14.4 ... Mehr zu Trait-Grenzen ... 789

       14.5 ... Zusammenfassung ... 794

15.  Iteratoren ... 797


       15.1 ... Wie Sie einen Iterator beziehen ... 798

       15.2 ... Iterator-Adapter ... 805

       15.3 ... Einen Iterator konsumieren ... 816

       15.4 ... Zusammenfassung ... 822

16.  Nebenläufige und asynchrone Programmierung ... 823


       16.1 ... Nebenläufige Programmierung ... 824

       16.2 ... Smart Pointer ... 876

       16.3 ... Asynchrone Programmierung ... 893

       16.4 ... Zusammenfassung ... 915

17.  Makros ... 917


       17.1 ... Deklarative Makros ... 917

       17.2 ... Prozedurale Makros ... 939

       17.3 ... Zusammenfassung ... 950

18.  Automatische Tests und Dokumentation ... 953


       18.1 ... Tests ... 954

       18.2 ... Rust-Projekte dokumentieren ... 966

       18.3 ... Zusammenfassung ... 979

19.  Unsafe Rust und das Foreign Function Interface ... 981


       19.1 ... Unsafe Rust ... 981

       19.2 ... Primitive Zeiger ... 987

       19.3 ... Union ... 998

       19.4 ... Foreign Function Interface ... 1001

       19.5 ... Zusammenfassung ... 1005

  Index ... 1007


3.2    Variablen


Bits und Bytes benötigen ein Zuhause. Damit die Zahlen, logischen Aussagen und Zeichenketten nicht haltlos in der Codesuppe umhertreiben, führen wir beim Programmieren Variablen ein. Das geschieht in zwei Schritten: bei der Deklaration des Bezeichners und dessen Typs sowie bei der Zuweisung eines Werts, der Initialisierung.

Die nächsten Abschnitte führen Sie durch die Eigenschaften von Variablen, insbesondere betrachten wie die Veränderlichkeit. Daneben behandeln wir Sprachfeatures wie die Typinferenz und das Shadowing. Die Frage nach der Veränderlichkeit von Variablen und Referenzen stellt einen zentralen Baustein von Rust dar: Grundsätzlich sind alle Variablen und Referenzen unveränderlich, nachdem Sie sie eingeführt haben. Erst das Schlüsselwort mut, kurz für mutable (zu Deutsch »veränderlich«), das Sie in der Deklaration einsetzen, ermöglicht eine erneute Zuweisung oder den schreibenden Zugriff.

Diese Eigenschaften führt ein Beispiel am Ende des Abschnitts vor. Dort werden Sie sehen, wie Sie in Rust eine Zeichenkette einlesen und in eine numerische Repräsentation bringen.

3.2.1    Die Deklaration einer Variable


Rust ist eine statisch typisierte Sprache. Jeder Wert, jede Variable, jede Funktion oder jede Struktur weist folglich einen festen Typ auf. Eine Variable kann nur jene Werte binden, die dem eigenen Datentyp entsprechen: Einer Variable vom Typ einer Ganzzahl, wie in Listing 3.1, können Sie zum Beispiel keine Zeichenkette und nicht einmal eine Fließkommazahl zuweisen!

Das statische Typsystem von Rust unterstützt das Verständnis von Rust-Code. Es fällt leichter, der Verwendung einer Variable im Code zu folgen, wenn der Typ verbindlich ist. Der Compiler erhält überdies Zusagen über deren Speicherbedarf und kann etwaige Optimierungen vornehmen. Sie können zwar den Typ und den Wert in einem Zug angeben (mehr dazu folgt in Abschnitt 3.2.2), aber oft steht der initiale Wert nicht fest, sodass Sie eine Variable zunächst deklarieren. Das erspart Ihnen, die Variable unnötigerweise für kurze Zeit zuzuweisen.

Angenommen, Sie möchten das Alter einer Person in Ihrem Programm einlesen. Damit Sie die Eingabe abspeichern können, führen Sie mit dem Schlüsselwort let die Variable alter ein. Etwa so:

fn main() {
let alter: i8;
}

Listing 3.1     Die Deklaration der Variable »alter«

Im Rumpf der Funktion main sehen Sie die Deklaration einer Variable. Das Schlüsselwort let eröffnet die Zeile und führt den Bezeichner (engl. Identifier) alter für die Variable ein. Darauf folgt der Doppelpunkt, mit dem Sie den Datentyp angeben. Diese Schreibweise unterscheidet sich deutlich von C und C++. Rust ist damit jedoch nicht allein, denn unter anderem setzen auch die Programmiersprachen Swift und Kotlin diese Syntax ein.

Der Typ i8 repräsentiert einen Integer von 8 Bit Breite. Integer ist das englische Wort für Ganzzahl. Neben i8 kennt Rust weitere numerische Werte, etwa den Datentyp i16 für eine Ganzzahl mit 16 Bit Breite. Die Typen stehen Ihnen in gängigen Bitgrößen (16, 32, 64 …) zur Auswahl. In Abschnitt 3.4.2, »Integer«, stelle ich Ihnen die anderen Ganzzahl-Datentypen vor.

Warum eigentlich »let«?

Ist Ihnen das Schlüsselwort let bereits in anderen Programmiersprachen begegnet? Woher kommt let? Tatsächlich fand sich dieses Schlüsselwort schon in BASIC! Im Jahr 1964 erschien dessen erstes Benutzerhandbuch und erklärte die Verwendung von let.

Der Ausdruck lässt sich ursprünglich auf die Mathematik zurückführen. Erinnern Sie sich an Aufgabenstellungen wie »Gegeben sei X=2 …«? Auf Englisch sagt man dann: »Let X=2«. Somit lehnte sich BASIC an die mathematische Ausdrucksweise an. Damals waren die Felder Mathematik und »Informatik« noch viel enger miteinander verbunden. Tatsächlich wurde eine Vorstellung von Software-Engineering als etwas Eigenständiges (abseits der Hardware) erst auf der NATO Conference 1968 in Garmisch herausgearbeitet!

Auch heute verwenden neben Rust einige Sprachen das Schlüsselwort let. So findet sich let außer in Rust ebenfalls in JavaScript (seit ES2015), Swift und C# (LINQ).

3.2.2    Eine Variable initialisieren


Die Variable alter mit dem Typ i8 wartet nun auf eine Wertzuweisung. Die Initialisierung als zweiter Schritt steht noch aus. Falls Sie eine Variable auslesen möchten, die Sie davor nicht initialisiert haben, meldet der Compiler sofort einen Fehler!

Hierdurch verhindert Rust ein undefiniertes Verhalten: Ohne den Fehler könnte das Speicherobjekt der Variable ein Bitmuster aufweisen, das mit dem Datentyp inkompatibel ist. Ein Beispiel: Der Datentyp String stellt durch seine Schnittstellen sicher, dass sich darin ausschließlich Zeichen befinden, die als UTF-8 kodiert worden sind. Würden Sie einen String deklarieren und vor der Initialisierung auslesen, würden sich im Speicherobjekt Bitmuster befinden, die UTF-8 nur zufällig oder gar nicht repräsentieren kann.

Folgendermaßen weisen Sie der Variable im Zuge der Deklaration einen Wert zu:

fn main() {
let alter: i8 = 42;
}

Listing 3.2     Die Variable »alter« initialisieren

Rust prüft an dieser Stelle zwei Dinge:

  • Weist der Wert, den Sie zuweisen wollen, den gleichen Datentyp auf oder müssten Sie ihn zuvor konvertieren?

  • Und wird der Wert in den Wertebereich des Zieltyps passen?

Im Beispiel entspricht das Literal 42 den Anforderungen, die ein vorzeichenbehafteter und acht Bit breiter i8 aufstellt, dessen positiver Maximalwert die Zahl 127 nicht übersteigen kann (Zweierkomplement, -128 bis 127).

Fehler wie Überläufe führen zu Sicherheits- und Speicherfehlern oder zumindest zu Verwirrung über das Ergebnis. Die Prüfung des Wertebereichs zur Kompilierzeit stellt vor diesem Hintergrund eine bedeutende Hilfe für den Programmierer dar. Während etwa C oder C++ höchstens eine Warnung über die implizite Umwandlung einer größeren Ganzzahl in eine kleinere ausgibt, meldet der Rust-Compiler einen Fehler! C oder C++-Linter können Sie freilich so konfigurieren, dass eine Warnung zu einem Fehler führt. Diese Maßnahme und die vorauseilenden Diskussionen im Team über das Für und Wider sparen Sie sich jedoch in Rust.

Eine Probe gefällig? Ändern Sie den Wert 42 zu 420, und reichen Sie das Programm an den Compiler weiter. Das Ergebnis:

error: literal out of range for `i8`
–> src/main.rs:6:19
|
6 | let alter: i8 = 420;
| ^^^
|
= note: `#[deny(overflowing_literals)]` on by default
= note: the literal `420` does not fit into the type `i8` whose range is
`-128..=127`
= help: consider using the type `i16` instead

Der Compiler markiert die Fehlerposition innerhalb des Codeschnipsels. Darunter befinden sich Informationen zu der Regel, die vom Code verletzt wurde, und Hinweise dazu, wie Sie das Problem beheben. Rust lässt Sie mit einem Fehler also nicht allein. Tatsächlich unternimmt das Sprach-Team von Rust große Anstrengungen, um die bestmöglichen Fehlermeldungen auszugeben.

3.2.3    Eine Variable mit dem Schlüsselwort »mut« veränderlich machen


Viele Programmiersprachen erlauben es Ihnen, eine Variable ohne Weiteres nach der Initialisierung zu ändern. In Rust müssen Sie dagegen bereits bei der Deklaration das Schlüsselwort mut einsetzen. Erst dann dürfen Sie eine Variable überschreiben. Rust-Variablen sind nach der Deklaration standardmäßig als unveränderlich eingefroren (engl. frozen), während Sie diesen Effekt etwa in C++ mit dem Schlüsselwort const erst aktiv herbeiführen müssen.

Sollten Sie die Unveränderlichkeit verletzen, weist der Compiler Sie umgehend darauf hin. Ein Beispiel:

fn main() {
let alter: i8 = 42;

// Fehler
alter = 21;
}

Listing 3.3     Die Variable ist vor der erneuten Zuweisung geschützt.

Der Compiler meldet, dass alter schon bei der Einführung einen Wert erhalten hat. Nachträglich dürfen Sie die Variable demnach nicht verändern:

error[E0384]: cannot assign twice to immutable variable `alter`
--> src/main.rs
|
2 | let alter: i8 = 42;
| -----
| |
| first assignment to `alter`
| help: consider making this binding mutable: `mut alter`
4 |...


Marburger, Marc
Marc Marburger hat langjährige Erfahrung in der App- und Softwareentwicklung. Angefangen hat er nach dem Abschluss in Informatik mit C++ und Assembler in der Industrieautomatisierung. Plattformübergreifende Apps beschäftigen ihn seit 2012: C# auf der Windows-Plattform, Xamarin, Flutter und Dart, Rust. Letztere setzt er täglich als freiberuflicher Softwareentwickler immer häufiger in Kundenprojekten ein.



Ihre Fragen, Wünsche oder Anmerkungen
Vorname*
Nachname*
Ihre E-Mail-Adresse*
Kundennr.
Ihre Nachricht*
Lediglich mit * gekennzeichnete Felder sind Pflichtfelder.
Wenn Sie die im Kontaktformular eingegebenen Daten durch Klick auf den nachfolgenden Button übersenden, erklären Sie sich damit einverstanden, dass wir Ihr Angaben für die Beantwortung Ihrer Anfrage verwenden. Selbstverständlich werden Ihre Daten vertraulich behandelt und nicht an Dritte weitergegeben. Sie können der Verwendung Ihrer Daten jederzeit widersprechen. Das Datenhandling bei Sack Fachmedien erklären wir Ihnen in unserer Datenschutzerklärung.