Zum Inhalt springen
Rust Anfรคnger 20 min

Funktionen in Rust

Definiere Funktionen mit fn, gib Werte zurueck, nutze Parameter und lerne den Unterschied zwischen expliziten return und impliziten Ausdruecken.

Aktualisiert:
Inhaltsverzeichnis

Funktionen in Rust

Funktionen sind das Grundgeruest jedes Rust-Programms. Du hast sie schon genutzt: main, println! (okay, das war ein Makro) und diverse Methoden.

Grundform

fn begruessen() {
    println!("Hallo!");
}

fn main() {
    begruessen();
}
  • fn leitet die Funktion ein
  • snake_case ist die Konvention fuer Funktionsnamen
  • () listet Parameter auf (hier: keine)
  • {} umschliesst den Funktionsrumpf

Parameter

Jeder Parameter braucht einen expliziten Typ - der Compiler leitet das hier nicht ab:

fn begruessen(name: &str) {
    println!("Hallo, {}!", name);
}

fn main() {
    begruessen("Anna");
    begruessen("Leo");
}

Mehrere Parameter werden mit Kommas getrennt:

fn addiere(a: i32, b: i32) {
    println!("{} + {} = {}", a, b, a + b);
}

Rueckgabewerte

Der Rueckgabetyp steht hinter ->:

fn addiere(a: i32, b: i32) -> i32 {
    a + b // keine Semikolon - das ist der Rueckgabewert!
}

fn main() {
    let ergebnis = addiere(3, 4);
    println!("{}", ergebnis); // 7
}

Explizites return

Du kannst auch return benutzen - besonders bei frueher Rueckkehr:

fn sicher_teilen(a: f64, b: f64) -> f64 {
    if b == 0.0 {
        return 0.0; // fruehzeitiges return
    }
    a / b
}

Expression vs. Statement

Erinnere dich an den Unterschied:

  • Statement endet mit ; und liefert keinen Wert
  • Expression liefert einen Wert

Eine Funktion gibt ihren letzten Expression-Wert zurueck. Wenn du ein Semikolon setzt, wird daraus ein Statement und die Funktion gibt () zurueck (den sogenannten โ€œunit typeโ€).

fn broken() -> i32 {
    5 + 3; // FEHLER: Semikolon macht das zum Statement
           // erwartet i32, bekommt ()
}

fn korrekt() -> i32 {
    5 + 3   // kein Semikolon
}

Funktionen mit mehreren Rueckgabewerten

Rust hat kein spezielles Multi-Return, aber Tupel erfuellen den Zweck:

fn min_max(zahlen: &[i32]) -> (i32, i32) {
    let mut min = zahlen[0];
    let mut max = zahlen[0];
    for &z in zahlen {
        if z < min { min = z; }
        if z > max { max = z; }
    }
    (min, max)
}

fn main() {
    let zahlen = [3, 1, 4, 1, 5, 9, 2, 6];
    let (min, max) = min_max(&zahlen);
    println!("min = {}, max = {}", min, max);
}

Bloecke als Ausdruecke

Innerhalb einer Funktion kannst du Bloecke als Ausdruecke nutzen:

fn main() {
    let wetter = "Regen";

    let kleidung = if wetter == "Sonne" {
        "T-Shirt"
    } else {
        "Jacke"
    };

    println!("Zieh eine {} an", kleidung);
}

Einfaches Beispiel: Zahl raten

fn ist_gerade(n: i32) -> bool {
    n % 2 == 0
}

fn beschreibe(n: i32) -> String {
    if ist_gerade(n) {
        format!("{} ist gerade", n)
    } else {
        format!("{} ist ungerade", n)
    }
}

fn main() {
    for i in 1..=5 {
        println!("{}", beschreibe(i));
    }
}

Kleine Stilregeln

  • snake_case fuer Namen: ist_gerade, nicht istGerade
  • Funktionen kurz halten: wenn eine Funktion viel mehr tut als ihr Name verspricht, zerlegen
  • Rueckgabetypen anzeigen - auch wenn Rust sie manchmal herleiten koennte, mach es dem Leser einfach

Uebersicht

  • fn name(parameter: Typ) -> Rueckgabe { ... }
  • Parameter brauchen immer einen expliziten Typ
  • Der letzte Ausdruck ohne Semikolon ist der Rueckgabewert
  • Frueher Ausstieg mit return
  • Tupel fuer Multi-Return
  • snake_case als Konvention

Als Naechstes wird es spannend: Das Ownership-System, das Herzstueck von Rust.

Zurรผck zum Rust Kurs