Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Note

Hinweis: Diese Inhalte wurden mit Unterstützung von Künstlicher Intelligenz erstellt und redaktionell überprüft (Transparenzhinweis gemäß Art. 50 EU AI Act).

Kapitel 01b: Der 8-Wochen-Trainingsplan (Sicht für Anfänger)

Dieser Plan führt dich in 8 Wochen von den ersten Schritten bis zur Erstellung sicherer und performanter Programme. Wenn du eine Woche erfolgreich abschließen willst, musst du die genannten Kernfragen beantworten und die Übungen fehlerfrei kompilieren können.


Woche 1: Das Fundament (Kapitel 01, 02 & 03)

  • Themen: Workspace-Setup, rustup, Cargo-Befehle, Variablen, Unveränderlichkeit, Shadowing und primitive Datentypen (Integers, Floats, Booleans, Chars, Tupel, Arrays).
  • Die Analogie: Variablen in Rust sind wie beschriftete Aufbewahrungsboxen. Standardmäßig sind sie mit Sekundenkleber verschlossen (unveränderlich). Wenn du den Inhalt austauschen willst, musst du die Box explizit als “wiederöffnbar” markieren (mut).
  • Das musst du wissen:
    1. Was ist der Unterschied zwischen cargo check und cargo build?
    2. Warum stürzt ein Programm ab, wenn ein Integer überläuft (Overflow), und wie verhindert Rust das?
    3. Was ist Shadowing und warum ist es nützlicher als eine Variable veränderbar zu machen?
  • Der Compilerfehler der Woche:
    fn main() {
        let x = 5;
        x = x + 1; // FEHLER!
    }
    Erklärung: Der Compiler meckert, weil x unveränderlich ist. Du musst entweder let mut x = 5; schreiben oder Shadowing nutzen: let x = x + 1; (wodurch eine neue Variable x die alte überschattet).
  • Praxis: Löse die Übungen in exercises/01_variables/.

Woche 2: Die Kletter-Sicherung: Ownership & Strings (Kapitel 04 & 05)

  • Themen: Ownership (Besitzregeln), Borrowing (Ausleihen), Referenzen (& und &mut), Lebensdauern (Lifetimes) Einführung, String vs. &str, UTF-8 Codierung.
  • Die Analogie: Ownership ist wie ein Bibliotheksbuch. Es gibt immer nur einen Eigentümer. Du kannst das Buch an deine Freunde ausleihen (Referenz). Wenn du es veränderbar ausleihst (&mut), darf in dieser Zeit kein anderer das Buch auch nur anschauen, damit niemand verwirrt wird, während Seiten umgeschrieben werden.
  • Das musst du wissen:
    1. Was passiert mit Heap-Daten, wenn ihr Besitzer (Owner) den Gültigkeitsbereich (Scope) verlässt?
    2. Warum darfst du nicht gleichzeitig eine veränderbare und eine unveränderbare Referenz auf dasselbe Datum haben?
    3. Was ist der Unterschied zwischen einem dynamischen String auf dem Heap und einem statischen Textausschnitt &str?
  • Der Compilerfehler der Woche:
    fn main() {
        let s1 = String::from("hallo");
        let s2 = s1; // Ownership wird verschoben (Move)
        println!("{}", s1); // FEHLER! s1 ist ungültig!
    }
    Erklärung: Da String Heap-Speicher besitzt, verschiebt Rust den Besitz an s2. s1 darf danach nicht mehr benutzt werden. Lösung: Nutze s1.clone(), wenn du eine echte Kopie willst, oder übergib eine Referenz: let s2 = &s1;.
  • Praxis: Löse die Übungen in exercises/02_ownership/ und exercises/03_strings/.

Woche 3: Daten strukturieren (Kapitel 06 & 10)

  • Themen: Standard-Collections (Vektoren Vec<T>, Key-Value-Speicher HashMap<K, V>), Structs (Klassische, Tupel- und Unit-like Structs), impl-Blöcke, Methoden und assoziierte Funktionen.
  • Die Analogie: Ein Struct ist wie der Steckbrief eines Rollenspielcharakters. Er fasst verschiedene Werte (Name, Lebenspunkte, Stärke) zusammen. Die Methoden im impl-Block sind die Fähigkeiten, die dieser Charakter ausführen kann.
  • Das musst du wissen:
    1. Wann benutzt man einen Vektor und wann eine HashMap?
    2. Was ist der Unterschied zwischen self, &self und &mut self in einer Methode?
    3. Wie erstellt man ein Struct, das keine Daten, sondern nur Verhalten besitzt (Unit-like Struct)?
  • Der Compilerfehler der Woche:
    struct Spieler { name: String }
    impl Spieler {
        fn gruesse(self) {
            println!("Hallo {}", self.name);
        }
    }
    fn main() {
        let s = Spieler { name: String::from("Thorsten") };
        s.gruesse(); // Ownership von s wird verbraucht!
        s.gruesse(); // FEHLER! s wurde bereits verschoben!
    }
    Erklärung: Da gruesse self (ohne &) entgegennimmt, verbraucht die Methode die Ownership des Structs. Lösung: Ändere die Methodensignatur zu fn gruesse(&self).
  • Praxis: Löse die Übungen in exercises/04_collections/ und exercises/05_structs/.

Woche 4: Funktionen & Fehlerbehandlung (Kapitel 07 & 09)

  • Themen: Funktionssignaturen, Ausdrücke vs. Anweisungen, Closures (Fn, FnMut, FnOnce), Fehlerbehandlung mit Option<T> und Result<T, E>, der ?-Operator, Panic vs. geordneter Abbruch.
  • Die Analogie: Fehlerbehandlung in Rust ist wie eine Paketlieferung. Der Bote wirft das Paket nicht einfach über den Zaun und hofft das Beste (Exception). Er gibt dir einen Karton (Result). Wenn du ihn öffnest, findest du entweder die bestellte Ware (Ok) oder einen Zettel mit der Schadensmeldung (Err). Du bist gezwungen, beides anzuschauen.
  • Das musst du wissen:
    1. Was unterscheidet einen Ausdruck (Expression, ohne Semikolon) von einer Anweisung (Statement, mit Semikolon)?
    2. Wie fängt man Fehler mit Pattern Matching ab, ohne das Programm abstürzen zu lassen?
    3. Warum sollte man unwrap() im produktiven Code strikt vermeiden?
  • Der Compilerfehler der Woche:
    #![allow(unused)]
    fn main() {
    fn hole_zahl(eingabe: &str) -> Result<i32, ParseIntError> {
        let zahl = eingabe.parse::<i32>()?; // Fehler wird weitergeleitet
        zahl // FEHLER!
    }
    }
    Erklärung: Rust erwartet am Ende der Funktion einen Rückgabetyp vom Typ Result. Da zahl ein nacktes i32 ist, schlägt die Kompilierung fehl. Lösung: Verpacke den Rückgabewert in Ok(zahl).
  • Praxis: Löse die Übungen in exercises/06_closures/ und exercises/07_errors/.

Woche 5: Kontrolle & Abstraktion (Kapitel 08 & 12)

  • Themen: Kontrollfluss, Pattern Matching im Detail, match (Exhaustivität), if let Syntax, Enums (Algebraische Datentypen mit Daten), Enums vs. Structs.
  • Die Analogie: Ein Enum ist wie eine Ampel. Sie kann rot, gelb oder grün sein, aber niemals alles gleichzeitig. In Rust kann jede dieser Farben sogar noch zusätzliche Informationen tragen (z.B. Rot zeigt die Sekunden an, bis es grün wird).
  • Das musst du wissen:
    1. Was bedeutet es, dass ein match-Block in Rust “exhaustiv” (vollständig) sein muss?
    2. Wann ist die if let-Schreibweise besser geeignet als ein volles match?
    3. Wie speichert man unterschiedliche Datentypen in den Varianten eines einzigen Enums?
  • Der Compilerfehler der Woche:
    #![allow(unused)]
    fn main() {
    enum Status { Aktiv, Inaktiv }
    fn pruefe(s: Status) {
        match s {
            Status::Aktiv => println!("Läuft!"),
            // FEHLER! Variante Inaktiv wird nicht abgedeckt!
        }
    }
    }
    Erklärung: Der Compiler zwingt dich, alle Varianten abzuhandeln. Du musst entweder Status::Inaktiv hinzufügen oder den Wildcard-Operator _ => {} als Standardfall nutzen.
  • Praxis: Löse die Übungen in exercises/08_control_flow/ und exercises/09_enums/.

Woche 6: Schnittstellen & Generics (Kapitel 11, 13 & 14)

  • Themen: Traits (Schnittstellen), Trait Bounds, impl Trait vs. dyn Trait, Generics in Funktionen, Structs und Enums, Module (mod), Cargo-Crates und Sichtbarkeiten (pub).
  • Die Analogie: Ein Trait ist wie ein Führerschein. Das Auto (Struct) fragt dich nicht, wer du bist. Es will nur wissen: “Hast du den Führerschein der Klasse B?” Wenn ja, darfst du fahren. Jeder Datentyp, der den Trait implementiert, darf an die Funktion übergeben werden.
  • Das musst du wissen:
    1. Was ist der Unterschied zwischen statischem Dispatch (impl Trait) und dynamischem Dispatch (dyn Trait)?
    2. Wie beschränkt man generische Typen so, dass sie nur bestimmte Methoden unterstützen (Trait Bounds)?
    3. Wie strukturierst du ein Projekt mit Modulen über mehrere Dateien hinweg?
  • Der Compilerfehler der Woche:
    #![allow(unused)]
    fn main() {
    trait Reden { fn sprich(&self); }
    fn sag_was<T>(ding: T) {
        ding.sprich(); // FEHLER!
    }
    }
    Erklärung: Der Compiler weiß nicht, ob der Typ T überhaupt sprechen kann. Lösung: Du musst dem Compiler mitteilen, dass T den Trait Reden besitzen muss: fn sag_was<T: Reden>(ding: T).
  • Praxis: Löse die Übungen in exercises/10_traits/ und exercises/11_generics/.

Woche 7: Fortgeschrittene Programmierung (Kapitel 15 & 16)

  • Themen: Iteratoren (Lazy Evaluation, map, filter, collect), Nebenläufigkeit (Threads), Message Passing (Channels), Shared State (Arc<Mutex<T>>), asynchrone Programmierung mit async/await.
  • Die Analogie: Ein Iterator ist wie ein Fließband in einer Fabrik. Die Kisten (Daten) liegen darauf, aber das Fließband bewegt sich erst, wenn am Ende jemand ein Paket abholt (collect). Vorher wird kein einziger Arbeitsschritt ausgeführt.
  • Das musst du wissen:
    1. Warum sind Iteratoren in Rust “lazy” (träge)?
    2. Was ist der Unterschied zwischen Rc<T> (Single-Thread) und Arc<T> (Multi-Thread)?
    3. Wie schützt ein Mutex Daten vor dem gleichzeitigen Zugriff durch mehrere Threads?
  • Der Compilerfehler der Woche:
    use std::thread;
    fn main() {
        let daten = vec![1, 2, 3];
        thread::spawn(|| {
            println!("{:?}", daten); // FEHLER!
        });
    }
    Erklärung: Der Thread könnte länger leben als die Funktion main. Daher erlaubt der Compiler nicht, dass der Thread eine Referenz auf daten ausleiht. Lösung: Nutze das move-Schlüsselwort vor der Closure, um die Ownership komplett in den Thread zu verschieben: thread::spawn(move || { ... }).
  • Praxis: Löse die Übungen in exercises/12_iterators/ und exercises/13_concurrency/.

Woche 8: Professionalisierung (Kapitel 17, 18, 19 & 20)

  • Themen: Metaprogrammierung mit Makros (macro_rules!), Testautomatisierung (#[test]), Dokumentation, Unsafe Rust (rohe Zeiger), FFI (Fremdsprachen-Interaktion), Infrastruktur des Buchs.
  • Die Analogie: Unsafe Rust ist wie das Ausschalten des ESP beim Auto. Du kannst jetzt driften und Dinge tun, die der Fahrassistent sonst verbietet. Aber wenn du gegen die Wand fährst, gibt es keine Knautschzone mehr. Du bist selbst dafür verantwortlich, dass nichts explodiert.
  • Das musst du wissen:
    1. Wann ist es legitim, einen unsafe-Block zu schreiben, und warum sollte man es im normalen Code vermeiden?
    2. Was ist der Unterschied zwischen Unit-Tests und Integrationstests in Rust?
    3. Wie generiert man Dokumentation direkt aus dem Quellcode?
  • Der Compilerfehler der Woche:
    fn main() {
        let mut x = 5;
        let r1 = &mut x as *mut i32; // Roher Zeiger erstellen
        println!("Wert: {}", *r1); // FEHLER!
    }
    Erklärung: Das Dereferenzieren eines rohen Zeigers (*r1) ist eine unsichere Operation, da der Compiler nicht garantieren kann, dass der Zeiger auf gültigen Speicher zeigt. Lösung: Die Dereferenzierung muss in einen unsafe-Block gepackt werden: unsafe { println!("{}", *r1); }.
  • Praxis: Löse die Übungen in exercises/14_macros/ und exercises/15_unsafe/.

Dein Wochen-Meilenstein (Der Selbsttest)

Um zu prüfen, ob du eine Woche erfolgreich abgeschlossen hast, beantworte die Kernfragen schriftlich und stelle sicher, dass alle Tests der Woche mit cargo test grün leuchten.

Note

Hinweis: Diese Inhalte wurden mit Unterstützung von Künstlicher Intelligenz erstellt und redaktionell überprüft (Transparenzhinweis gemäß Art. 50 EU AI Act).