Skip to content

Modul 2 — Sprachgrundlagen: Typen, Funktionen, Kontrollfluss

Shadowing, skalare und zusammengesetzte Typen, der Tail-Expression-Trick, if/match/loop — mit Laufzeit-Panics, die du im Playground selbst auslösen kannst.

Fortschritt0/4 Lektionen
5

Shadowing statt Mutation

Lektion 5 von 4

Rust erlaubt es, eine Variable neu zu deklarieren — die alte Bindung wird überschattet:

fn main() {
    let x = 5;
    let x = x + 1;       // neue Bindung, alte ist ab hier unsichtbar
    println!("{x}");     // 6
}

Der feine Unterschied zu mut: Shadowing erzeugt eine neue Bindung, die auch einen anderen Typ haben darf:

let spaces = "   ";      // &str
let spaces = spaces.len(); // usize — komplett anderer Typ, völlig legal

Mit mut müsste der Typ gleich bleiben. Shadowing ist der idiomatische Weg für "wandle einen Wert Schritt für Schritt um", ohne Namen wie spaces2 zu erzeugen.

Shadowing ≠ Mutation

Shadowing: neue Bindung, neuer Typ möglich, alte Bindung bleibt unverändert (bis zum Scope-Ende). mut: dieselbe Bindung, derselbe Typ, Wert ändert sich.

Praxis-Szenario: Der Datenimport

Ein CSV-Import liefert alle Werte als Strings. Der Prototyp rechnet mit Rohwerten weiter — das Ergebnis stimmt nie. Dein Kollege will Parsing-Code überall einbauen; du schlägst Shadowing mit sauberen Typen vor.

Übung: Variablen überschatten

🧩 Übung m2-l5-e1

🔮 Vorhersage: Was passiert beim Ausführen?

Editor wird geladen…
KI-Tutor (sokratisch — keine Komplettlösungen)

✅ Wichtige Erkenntnisse

0/2

Haken setzen, um deinen Lernfortschritt zu markieren:

6

Datentypen: Skalare, Tupel, Arrays

Lektion 6 von 4

Skalare

i8–i128/u8–u128 (Vorzeichen ja/nein), f32/f64, bool, char. Der Default ist i32 bzw. f64.

fn main() {
    let a: i32 = -42;
    let b: u8 = 255;      // größter u8-Wert
    let c = 2.5_f64;
    let letter = 'z';     // char: EIN Unicode-Skalarpunkt, einfache Quotes
    let s = "z";          // &str: String-Slice, doppelte Quotes
}

Integer-Overflow: Panik im Debug, Wrap im Release

fn inc(x: u8) -> u8 {
    x + 1
}

fn main() {
    println!("{}", inc(255));
}

Typkorrekt — der Compiler akzeptiert es. Aber im Debug-Build bricht das Programm zur Laufzeit mit attempt to add with overflow ab, im Release- Build wrappt es still auf 0. Probiere es im Playground aus!

Overflow selbst auslösenBereit
Editor wird geladen…

Tupel und Arrays

fn main() {
    let t = (1, 2.5, "three"); // Tupel: gemischt, feste Größe
    println!("{}", t.0);       // 1 — Zugriff per Punkt-Index

    let a = [1, 2, 3];         // Array: gleichartig, Stack, feste Größe
    println!("{}", a[1]);      // 2 — Laufzeit-Indexcheck
}

Indexieren ist zur Laufzeit geprüft: a[5] kompiliert (bei Variable als Index), paniziert aber mit index out of bounds — Rust liest nie fremden Speicher.

Übung: Tupel-Zugriff

🧩 Übung m2-l6-e1

🔮 Vorhersage: Was passiert beim Ausführen?

Editor wird geladen…
KI-Tutor (sokratisch — keine Komplettlösungen)

✅ Wichtige Erkenntnisse

0/3

Haken setzen, um deinen Lernfortschritt zu markieren:

7

Funktionen & die Tail Expression

Lektion 7 von 4

fn add(a: i32, b: i32) -> i32 {
    a + b
}

Kein return, kein Semikolon: Die letzte Anweisung ohne Semikolon ist der Rückgabewert. Die Semikolon-Regel ist die wichtigste neue Gewohnheit für Einsteiger:

fn broken(a: i32, b: i32) -> i32 {
    a + b;   // ❌ Semikolon macht daraus eine Anweisung vom Typ ()
}            // ❌ E0308: expected i32, found ()

Parameter-Typen sind Pflicht, Rückgabetyp steht nach dem Pfeil. () (unit) ist Rusts "nichts" — der Typ von Anweisungen.

Semikolon = Wert wegwerfen

expr; wertet expr aus und verwirft das Ergebnis (Typ ()). Lässt du das Semikolon weg, ist der Ausdruck der Wert. Das gilt in Blöcken, if-Armen und Funktionskörpern gleichermaßen.

Übung: Tail-Expression in Funktionen

🧩 Übung m2-l7-e1

🔮 Vorhersage: Was passiert beim Ausführen?

Editor wird geladen…
KI-Tutor (sokratisch — keine Komplettlösungen)

✅ Wichtige Erkenntnisse

0/3

Haken setzen, um deinen Lernfortschritt zu markieren:

8

Kontrollfluss: if, match, loop

Lektion 8 von 4

if ist ein Ausdruck

fn main() {
    let a = 3;
    let b = 7;
    let max = if a > b { a } else { b };
    println!("{max}"); // 7
}

Beide Arme müssen denselben Typ haben — sonst kann der Compiler den Typ des Gesamtausdrucks nicht bestimmen. Keine Klammern um die Bedingung, dafür geschweifte Klammern Pflicht.

match: erschöpfend by Design

fn main() {
    let n = 3;
    let label = match n {
        1 => "one",
        3 => "three",
        _ => "many",
    };
    println!("{label}"); // three
}

Der Compiler verlangt Vollständigkeit: Jeder mögliche Wert muss von einem Arm abgedeckt sein. Der Wildcard-Arm _ fängt den Rest. Vergessen wir ihn, kompiliert es nicht — eine ganze Fehlerklasse zur Compile-Zeit erledigt.

loop mit Wert

fn main() {
    let mut count = 0;
    let result = loop {
        count += 1;
        if count == 10 {
            break count * 2; // loop kann einen Wert liefern
        }
    };
    println!("{result}"); // 20
}

while und for (über Bereiche: for i in 0..5) ergänzen das Repertoire — loop ist aber die einzige Schleife, die als Ausdruck einen Wert liefern kann.

Übung: match und loop

🧩 Übung m2-l8-e1

🔮 Vorhersage: Was passiert beim Ausführen?

Editor wird geladen…
KI-Tutor (sokratisch — keine Komplettlösungen)

✅ Wichtige Erkenntnisse

0/3

Haken setzen, um deinen Lernfortschritt zu markieren:

This site uses essential cookies for Stripe payments. No tracking cookies.