Modul 4 — Structs & Methoden
Structs: Daten bündeln
Bisher hatten deine Programme lose Variablen. Ein echtes Programm bündelt zusammengehörige Daten: ein Buch hat Titel und Seitenzahl, ein Nutzer Name und E-Mail. Genau dafür sind Structs da — Rusts Version von "Record":
struct Book {
title: String,
pages: u32,
}
fn main() {
let b = Book {
title: String::from("Der Rust-Weg"),
pages: 312,
};
println!("{} hat {} Seiten", b.title, b.pages);
}
Ein Struct-Literal nennt jedes Feld mit Namen. Vergisst du eines, meckert der Compiler sofort (E0063) — statt zur Laufzeit ein halb initialisiertes Objekt zu erzeugen, wie es in vielen Sprachen möglich wäre.
Kurzschreibweisen und Update-Syntax
struct Book {
title: String,
pages: u32,
}
fn main() {
let title = String::from("Kurze Note");
let pages = 2;
let note = Book { title, pages }; // Feld-Init-Kurzschreibweise
let book = Book {
pages: 312,
..note // REST aus note übernehmen
};
println!("{book:?}");
}
Zwei Übliche Werkzeuge: Book { title, pages } nutzt die Variable mit
gleichem Namen (Feld-Init-Kurzschreibweise). ..note übernimmt alle
übrigen Felder aus einer anderen Instanz.
Debug für println! ({:?})
println!("{b:?}") gibt die ganze Struktur aus — dafür braucht der Typ das
Trait Debug. Du hängst einfach #[derive(Debug)] über die Struct-Definition
und der Compiler generiert die Ausgabe für dich.
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 3, y: -2 };
println!("{p:?}");
}
Achtung: Structs bewegen sich wie Strings
Structs sind Copy-Typen nur, wenn alle Felder Copy sind. Sobald ein
String-Feld dabei ist, verschiebt eine Zuweisung den Besitz — und mit der
Update-Syntax ..base moved du die non-Copy-Felder aus base heraus
("partial move"). Danach ist base als Ganzes verbraucht.
📝 Schnellprüfung
Was gibt dieses Programm aus?
Übung: das erste Buch
🧩 Übung m4-l1-e1
🔮 Vorhersage: Was passiert beim Ausführen?
✅ Wichtige Erkenntnisse
Haken setzen, um deinen Lernfortschritt zu markieren:
Methoden & impl-Blöcke
Daten ohne Verhalten sind nur halb brauchbar. Methoden kommen in einen impl-Block — getrennt von den Daten, aber dem Typ zugeordnet:
struct Counter {
count: u32,
}
impl Counter {
fn new() -> Self {
Counter { count: 0 }
}
fn increment(&mut self) {
self.count += 1;
}
fn value(&self) -> u32 {
self.count
}
}
fn main() {
let mut c = Counter::new();
c.increment();
c.increment();
println!("count = {}", c.value());
}
Die drei Empfänger
Der erste Parameter einer Methode entscheidet, was sie mit der Instanz anstellen darf:
| Empfänger | Bedeutung | Typisch für |
|---|---|---|
&self | leiht read-only | Berechnen, Lesen, Anzeigen |
&mut self | leiht veränderlich | Zustand ändern wie increment |
self | übernimmt Besitz | Verbrauchende Umwandlungen, Builder |
Counter::new hat gar kein self — es ist eine assoziierte Funktion
(der Konstruktor-Klassiker), aufgerufen mit Counter::new(). Self ist die
Abkürzung für den Typ selbst.
Wann welcher Empfänger?
Default-Frage: Braucht die Methode den Besitz? Nein → &self (lesen) oder
&mut self (ändern). self nur, wenn der Aufrufer den Wert danach bewusst
nicht mehr braucht — der Compiler erzwingt das für beide Seiten.
Rust hat keinen versteckten this-Zeiger: Die Methode bekommt die Instanz
explizit als ersten Parameter. Methodenaufrufe mit Punkt-Syntax
(c.increment()) dereferenzieren automatisch — egal ob c, &mut c oder
ein Box-Smartpointer dahintersteckt.
📝 Schnellprüfung
Wie erstellst du einen neuen Counter ohne bestehende Instanz?
Übung: der Zähler
🧩 Übung m4-l2-e1
🔮 Vorhersage: Was passiert beim Ausführen?
✅ Wichtige Erkenntnisse
Haken setzen, um deinen Lernfortschritt zu markieren:
Tuple-Structs & das Newtype-Muster
Manchmal brauchen Felder keine Namen — die Reihenfolge reicht:
struct Meters(u32);
struct Seconds(u32);
fn main() {
let dist = Meters(120);
println!("{} Meter", dist.0); // Zugriff per Index
}
Das ist eine Tuple-Struct: Meters(120) sieht aus wie ein Funktionsaufruf
und speichert einen u32 — aber als eigenen Typ.
Newtype: Null Kosten, volle Sicherheit
Meters und Seconds verhalten sich exakt wie u32 im Speicher (gleiche
Größe, gleiche Geschwindigkeit). Trotzdem kann der Compiler sie nicht
verwechseln:
struct Meters(u32);
struct Seconds(u32);
impl Meters {
fn add(self, other: Meters) -> Meters {
Meters(self.0 + other.0)
}
}
fn main() {
let a = Meters(120);
let b = Meters(80);
println!("{} m", a.add(b).0);
// a.add(Seconds(5)); // ❌ expected `Meters`, found `Seconds`
}
Dieses Muster heißt Newtype und ist in echtem Rust allgegenwärtig: IDs, Einheiten, Versionen — alles bekommt einen eigenen Typ, und „Vertauscher“-Bugs werden zu Compile-Fehlern. Mars Climate Orbiter lässt grüßen.
Unit-Structs: Typen ohne Daten
struct WIP;
fn main() {
let _state = WIP;
}
Ein Unit-Struct hat gar keine Felder. Er dient als Markierung: Der Typ SELBST trägt die Information („hier ist noch nichts drin“), ohne ein Byte Daten zu kosten. Später im Kurs sehen wir ihn in State-Maschinen wieder.
📝 Schnellprüfung
Warum ist `a.add(Seconds(5))` ein Compile-Fehler, obwohl beide u32 sind?
Übung: Meters zusammenzählen
🧩 Übung m4-l3-e1
🔮 Vorhersage: Was passiert beim Ausführen?
✅ Wichtige Erkenntnisse
Haken setzen, um deinen Lernfortschritt zu markieren:
Mini-Projekt: Inventar
Zeit, alles zusammenzubringen: ein Inventar aus Items mit Mengen — Structs, Vec, Methoden, associated function. Erst das Szenario aus dem Arbeitsleben:
🏢 Praxis-Szenario: Die Lagerverwaltung
Dein Team pflegt Bestände in einer Excel-Liste: zweimal derselbe Artikel, Tippfehler in den Namen, Summen per Hand. Bei der Inventur stimmt nichts. Du schlägst vor, die Domäne als Typen zu modellieren: Item mit Name und Menge, Inventory als Verwaltung mit Methoden — falsche Summen werden strukturell unmöglich.
Das Gerüst hast du teilweise schon aus Lektion 14. Die Übung lässt dich den letzten Teil selbst bauen — mit dem XP-Lohn fürs lösen ohne Hilfe:
🧩 Übung m4-l4-e1
🔮 Vorhersage: Was passiert beim Ausführen?
Was du in Modul 4 geschafft hast
- Structs bündeln Daten mit benannten Feldern und initialisieren vollständig
- impl-Blöcke trennen Verhalten von Daten; Empfänger wählen die Rechte
- Newtypes machen Einheiten zur Compile-Zeit-Sache
- Ein kleines Domänenmodell (Item + Inventory) steht und ist erweiterbar
✅ Wichtige Erkenntnisse
Haken setzen, um deinen Lernfortschritt zu markieren:
Modul-Checkpoint
Checkpoint: Modul 4
Bestehensgrenze: 80 %. Der Versuch wird bewertet und zählt für das Modul-Gate.
Bereit, dein Wissen zu testen?
Probiere unsere Übungsprüfungen mit Hunderten von realistischen Fragen aus.
Üben starten →