Modul 11 — Smart Pointer & der Heap
Box: der Zeiger auf den Heap
Bisher lebten deine Werte auf dem Stack — mit bekannter, fester Größe. Box verschiebt den Inhalt auf den Heap; auf dem Stack bleibt nur ein dünner Zeiger, der den Wert besitzt:
fn main() {
let gross = Box::new([0u8; 4096]);
println!("erstes Byte: {}", gross[0]);
println!("Länge: {}", gross.len());
}
Dank Deref verhält sich der Box-Zeiger wie der Wert selbst: Index-Zugriff,
Methoden, Dereferenzierung mit * — alles funktioniert direkt. Beim
Drop wird der Heap-Speicher freigegeben. Ein Besitzer, eine Freigabe —
dein Ownership-Modell, nur mit Umleitung über den Heap.
Zwei klassische Einsatzfälle:
- Rekursive Typen — eine verkettete Liste enthält sich selbst; ohne Box kennt der Compiler die Größe nicht (E0072). Der Box-Zeiger hat feste Größe und bricht den Kreis. Die Übung baut genau das.
- Einheitliche Fehler — ein Tool, das Datei- UND Parse-Fehler wirft:
use std::error::Error;
fn alter_von(zeile: &str) -> Result<u32, Box<dyn Error>> {
let (_, wert) = zeile
.split_once('=')
.ok_or("Zeile ohne Gleichheitszeichen")?;
Ok(wert.trim().parse::<u32>()?)
}
fn main() {
println!("{:?}", alter_von("alter=30"));
}
Box<dyn Error> ist der pragmatische Fehlertyp für Programme: jede
konkrete Fehlerart passt hinein, ? konvertiert automatisch.
Wann Box?
- Rekursive Datentypen — der Größe-Kreisbruch
- Große Werte, die du verschieben willst, ohne zu kopieren
- Trait-Objekte — die Fehler-Box mit dyn Error versteckt die Größe hinter einem Zeiger
Übung: die rekursive Liste
🧩 Übung m11-l1-e1
🔮 Vorhersage: Was passiert beim Ausführen?
✅ Wichtige Erkenntnisse
Haken setzen, um deinen Lernfortschritt zu markieren:
Rc: Besitz teilen
Box hat genau einen Besitzer. Manchmal brauchen denselben Wert aber mehrere: eine Konfiguration, die zwei Module lesen; ein Knoten, auf den mehrere Datenstrukturen zeigen. Rc (Reference Counted) erlaubt mehrere Besitzer — zur Laufzeit gezählt:
use std::rc::Rc;
fn main() {
let konfig = Rc::new(vec![10, 20, 30]);
let kopie_a = Rc::clone(&konfig);
let kopie_b = Rc::clone(&konfig);
println!("a-Summe: {}", kopie_a.iter().sum::<u32>());
println!("Besitzer: {}", Rc::strong_count(&konfig));
}
Rc::clone kopiert NICHTS — es erhöht nur den Zähler. Alle drei Bindungen
zeigen auf denselben Heap-Wert; fällt der letzte Besitzer weg, wird er
freigegeben. strong_count sagt dir, wie viele es gerade sind.
Der Preis: Rc-Inhalte sind nur lesbar. Mit mehreren Besitzern könnte eine Änderung über einen Zeiger invalidieren, was ein anderer gerade liest — genau das verbietet Rusts Aliasing-Regel. Für Mutation braucht es den nächsten Baustein.
Und Rc bleibt zuhause: sein Zähler ist nicht atomar, also ist Rc ausdrücklich nicht thread-safe. Für Threads gibt es Arc — dasselbe Muster mit atomarem Zähler (Modul 12).
📝 Schnellprüfung
Was kostet Rc::clone(&konfig) zur Laufzeit?
Übung: geteilter Besitz
🧩 Übung m11-l2-e1
🔮 Vorhersage: Was passiert beim Ausführen?
✅ Wichtige Erkenntnisse
Haken setzen, um deinen Lernfortschritt zu markieren:
RefCell: Mutation zur Laufzeit
Die Borrow-Regeln gelten zur Compile-Zeit — meistens gut, manchmal zu streng: Ein Logger wird von vielen Stellen geteilt und darf trotzdem wachsen. RefCell verlagert die Prüfung in die Laufzeit:
use std::cell::RefCell;
fn main() {
let log = RefCell::new(Vec::new());
log.borrow_mut().push(String::from("start"));
{
let lesend = log.borrow();
println!("Einträge: {}", lesend.len());
}
log.borrow_mut().push(String::from("ende"));
println!("{:?}", log.borrow());
}
borrow() leiht lesend (beliebig oft), borrow_mut() exklusiv. Die
Regel bleibt dieselbe — Shared XOR Exclusive — nur dass RefCell sie
zählt: ein Konflikt paniziert sofort, mit präziser Meldung. Der
innere Block gibt den lesenden Borrow zurück, bevor borrow_mut() kommt.
Die Kombo aus beiden Welten ist der Klassiker: Rc + RefCell — Rc teilt den Besitz, RefCell erlaubt die Änderung durch den gemeinsamen Handle. Geteilter, veränderlicher Zustand ohne unsafe.
Runtime-Borrows im Griff
- borrow() = lesender Zähler hoch; borrow_mut() = exklusiv, paniziert bei Konflikt
- try_borrow_mut() liefert Result statt Panic — für abfangbare Konflikte
- Rc + RefCell = geteilter, veränderlicher Zustand (single-threaded)
Übung: der Runtime-Borrow
🧩 Übung m11-l3-e1
🔮 Vorhersage: Was passiert beim Ausführen?
✅ Wichtige Erkenntnisse
Haken setzen, um deinen Lernfortschritt zu markieren:
Mini-Projekt: der geteilte Tracker
Das Szenario vorweg — dann baust du die Lösung:
🏢 Praxis-Szenario: Die Ausgabenliste, die jeder sehen darf
Dein Team bucht Ausgaben (Server, Domain, Kaffee) in einer geteilten Liste. Bisher: drei Kollegen, drei eigene Kopien der Liste, dreimal wöchentlich manuell zusammenführen — und dreimal pro Monat falsche Summen. Die Anforderung: EINE Liste im Speicher, auf die mehrere Stellen schreiben und lesen, ohne dass sich jemand Kopien zieht oder die Summe aus dem Takt kommt.
🧩 Übung m11-l4-e1
🔮 Vorhersage: Was passiert beim Ausführen?
Was du in Modul 11 geschafft hast
- Box: Heap mit einem Besitzer — inklusive Rekursions-Ausweg und dyn Error
- Rc: geteilter Besitz per Zähler — read-only, billig zu klonen
- RefCell: Borrow-Regeln zur Laufzeit; Rc + RefCell = geteilter veränderlicher Zustand
- Ein Tracker mit EINER Quelle der Wahrheit
✅ Wichtige Erkenntnisse
Haken setzen, um deinen Lernfortschritt zu markieren:
Modul-Checkpoint
Checkpoint: Modul 11
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 →