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:
- Was ist der Unterschied zwischen
cargo checkundcargo build? - Warum stürzt ein Programm ab, wenn ein Integer überläuft (Overflow), und wie verhindert Rust das?
- Was ist Shadowing und warum ist es nützlicher als eine Variable veränderbar zu machen?
- Was ist der Unterschied zwischen
- Der Compilerfehler der Woche:
Erklärung: Der Compiler meckert, weilfn main() { let x = 5; x = x + 1; // FEHLER! }xunveränderlich ist. Du musst entwederlet mut x = 5;schreiben oder Shadowing nutzen:let x = x + 1;(wodurch eine neue Variablexdie 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,Stringvs.&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:
- Was passiert mit Heap-Daten, wenn ihr Besitzer (Owner) den Gültigkeitsbereich (Scope) verlässt?
- Warum darfst du nicht gleichzeitig eine veränderbare und eine unveränderbare Referenz auf dasselbe Datum haben?
- Was ist der Unterschied zwischen einem dynamischen
Stringauf dem Heap und einem statischen Textausschnitt&str?
- Der Compilerfehler der Woche:
Erklärung: Dafn main() { let s1 = String::from("hallo"); let s2 = s1; // Ownership wird verschoben (Move) println!("{}", s1); // FEHLER! s1 ist ungültig! }StringHeap-Speicher besitzt, verschiebt Rust den Besitz ans2.s1darf danach nicht mehr benutzt werden. Lösung: Nutzes1.clone(), wenn du eine echte Kopie willst, oder übergib eine Referenz:let s2 = &s1;. - Praxis: Löse die Übungen in
exercises/02_ownership/undexercises/03_strings/.
Woche 3: Daten strukturieren (Kapitel 06 & 10)
- Themen: Standard-Collections (Vektoren
Vec<T>, Key-Value-SpeicherHashMap<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:
- Wann benutzt man einen Vektor und wann eine HashMap?
- Was ist der Unterschied zwischen
self,&selfund&mut selfin einer Methode? - Wie erstellt man ein Struct, das keine Daten, sondern nur Verhalten besitzt (Unit-like Struct)?
- Der Compilerfehler der Woche:
Erklärung: Dastruct 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! }gruesseself(ohne&) entgegennimmt, verbraucht die Methode die Ownership des Structs. Lösung: Ändere die Methodensignatur zufn gruesse(&self). - Praxis: Löse die Übungen in
exercises/04_collections/undexercises/05_structs/.
Woche 4: Funktionen & Fehlerbehandlung (Kapitel 07 & 09)
- Themen: Funktionssignaturen, Ausdrücke vs. Anweisungen, Closures (Fn, FnMut, FnOnce), Fehlerbehandlung mit
Option<T>undResult<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:
- Was unterscheidet einen Ausdruck (Expression, ohne Semikolon) von einer Anweisung (Statement, mit Semikolon)?
- Wie fängt man Fehler mit Pattern Matching ab, ohne das Programm abstürzen zu lassen?
- Warum sollte man
unwrap()im produktiven Code strikt vermeiden?
- Der Compilerfehler der Woche:
Erklärung: Rust erwartet am Ende der Funktion einen Rückgabetyp vom Typ#![allow(unused)] fn main() { fn hole_zahl(eingabe: &str) -> Result<i32, ParseIntError> { let zahl = eingabe.parse::<i32>()?; // Fehler wird weitergeleitet zahl // FEHLER! } }Result. Dazahlein nacktesi32ist, schlägt die Kompilierung fehl. Lösung: Verpacke den Rückgabewert inOk(zahl). - Praxis: Löse die Übungen in
exercises/06_closures/undexercises/07_errors/.
Woche 5: Kontrolle & Abstraktion (Kapitel 08 & 12)
- Themen: Kontrollfluss, Pattern Matching im Detail,
match(Exhaustivität),if letSyntax, 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:
- Was bedeutet es, dass ein
match-Block in Rust “exhaustiv” (vollständig) sein muss? - Wann ist die
if let-Schreibweise besser geeignet als ein vollesmatch? - Wie speichert man unterschiedliche Datentypen in den Varianten eines einzigen Enums?
- Was bedeutet es, dass ein
- Der Compilerfehler der Woche:
Erklärung: Der Compiler zwingt dich, alle Varianten abzuhandeln. Du musst entweder#![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! } } }Status::Inaktivhinzufügen oder den Wildcard-Operator_ => {}als Standardfall nutzen. - Praxis: Löse die Übungen in
exercises/08_control_flow/undexercises/09_enums/.
Woche 6: Schnittstellen & Generics (Kapitel 11, 13 & 14)
- Themen: Traits (Schnittstellen), Trait Bounds,
impl Traitvs.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:
- Was ist der Unterschied zwischen statischem Dispatch (
impl Trait) und dynamischem Dispatch (dyn Trait)? - Wie beschränkt man generische Typen so, dass sie nur bestimmte Methoden unterstützen (Trait Bounds)?
- Wie strukturierst du ein Projekt mit Modulen über mehrere Dateien hinweg?
- Was ist der Unterschied zwischen statischem Dispatch (
- Der Compilerfehler der Woche:
Erklärung: Der Compiler weiß nicht, ob der Typ#![allow(unused)] fn main() { trait Reden { fn sprich(&self); } fn sag_was<T>(ding: T) { ding.sprich(); // FEHLER! } }Tüberhaupt sprechen kann. Lösung: Du musst dem Compiler mitteilen, dassTden TraitRedenbesitzen muss:fn sag_was<T: Reden>(ding: T). - Praxis: Löse die Übungen in
exercises/10_traits/undexercises/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 mitasync/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:
- Warum sind Iteratoren in Rust “lazy” (träge)?
- Was ist der Unterschied zwischen
Rc<T>(Single-Thread) undArc<T>(Multi-Thread)? - Wie schützt ein
MutexDaten vor dem gleichzeitigen Zugriff durch mehrere Threads?
- Der Compilerfehler der Woche:
Erklärung: Der Thread könnte länger leben als die Funktionuse std::thread; fn main() { let daten = vec![1, 2, 3]; thread::spawn(|| { println!("{:?}", daten); // FEHLER! }); }main. Daher erlaubt der Compiler nicht, dass der Thread eine Referenz aufdatenausleiht. Lösung: Nutze dasmove-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/undexercises/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:
- Wann ist es legitim, einen
unsafe-Block zu schreiben, und warum sollte man es im normalen Code vermeiden? - Was ist der Unterschied zwischen Unit-Tests und Integrationstests in Rust?
- Wie generiert man Dokumentation direkt aus dem Quellcode?
- Wann ist es legitim, einen
- Der Compilerfehler der Woche:
Erklärung: Das Dereferenzieren eines rohen Zeigers (fn main() { let mut x = 5; let r1 = &mut x as *mut i32; // Roher Zeiger erstellen println!("Wert: {}", *r1); // FEHLER! }*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 einenunsafe-Block gepackt werden:unsafe { println!("{}", *r1); }. - Praxis: Löse die Übungen in
exercises/14_macros/undexercises/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).