EMZETT.
Login

Kurz: fn gruss(name: &str) { // kein Rückgabetyp: gibt () zurück println!(“Hallo {name}!”); }

Teil des Kurses Rust

Kapitel 4 von 13 im Kurs Rust. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

fn addiere(a: i32, b: i32) -> i32 {
    a + b                              // letzter Ausdruck ohne Semikolon ist der Rückgabewert
}
 
fn gruss(name: &str) {                 // kein Rückgabetyp: gibt () zurück
    println!("Hallo {name}!");
}
 
fn teile(a: i32, b: i32) -> (i32, i32) {
    (a / b, a % b)                     // Tupel als Mehrfach-Rückgabe
}
 
fn betrag(x: i32) -> i32 {
    if x < 0 { return -x; }            // früher Rückgabe mit return
    x
}
 
fn main() {
    println!("{}", addiere(3, 4));
    gruss("Mia");
    let (q, r) = teile(17, 5);
    println!("{q} {r} {}", betrag(-9));
}

Ausgabe:

7
Hallo Mia!
3 2 9

Das letzte Element einer Funktion ohne Semikolon ist ihr Ergebnis. Mit Semikolon wird daraus eine Anweisung und der Typ wäre (): ein klassischer Anfängerfehler.

fn falsch(x: i32) -> i32 {
    x + 1;
}

Fehlermeldung:

error[E0308]: mismatched types

Funktionen als Werte

fn quadrat(x: i32) -> i32 { x * x }
 
fn wende(f: fn(i32) -> i32, x: i32) -> i32 { f(x) }
 
fn main() {
    println!("{}", wende(quadrat, 7));
    let funktionen: [fn(i32) -> i32; 2] = [quadrat, |x| x + 1];
    for f in funktionen { print!("{} ", f(5)); }
    println!();
}

Ausgabe:

49
25 6

Closures

Eine Closure ist eine namenlose Funktion, die Variablen aus ihrer Umgebung einfangen kann:

fn main() {
    let faktor = 3;
    let mal = |x: i32| x * faktor;                 // fängt faktor ein
    let addiere = |a, b| a + b;                    // Typen werden erkannt
    println!("{} {}", mal(7), addiere(1, 2));
 
    let mut zaehler = 0;
    let mut erhoehe = || { zaehler += 1; zaehler };   // verändert eingefangene Variable
    erhoehe();
    erhoehe();
    println!("{}", zaehler);
 
    let zahlen = vec![1, 2, 3, 4, 5, 6];
    let gerade: Vec<i32> = zahlen.iter().filter(|&&x| x % 2 == 0).cloned().collect();
    let quadrate: Vec<i32> = zahlen.iter().map(|x| x * x).collect();
    println!("{gerade:?} {quadrate:?}");
 
    let schwelle = 3;
    let groesser = zahlen.iter().filter(|&&x| x > schwelle).count();
    println!("{groesser}");
 
    let mut woerter = vec!["Birne", "Apfel", "Kiwi"];
    woerter.sort_by_key(|w| w.len());
    println!("{woerter:?}");
}

Ausgabe:

21 3
2
[2, 4, 6] [1, 4, 9, 16, 25, 36]
3
["Kiwi", "Birne", "Apfel"]

Closures gibt es in drei Arten, je nachdem was sie mit den eingefangenen Werten tun: Fn (nur lesen), FnMut (verändern), FnOnce (verbrauchen). Der Compiler wählt automatisch.

Closures zurückgeben und übergeben

fn erzeuge_mal(faktor: i32) -> impl Fn(i32) -> i32 {
    move |x| x * faktor                      // move: Closure besitzt faktor
}
 
fn wende_zweimal<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 { f(f(x)) }
 
fn box_beispiel() -> Box<dyn Fn(i32) -> i32> {       // verschiedene Closures in einem Typ
    Box::new(|x| x + 100)
}
 
fn main() {
    let dreifach = erzeuge_mal(3);
    println!("{} {}", dreifach(5), wende_zweimal(&dreifach, 2));
    println!("{}", box_beispiel()(1));
    let operationen: Vec<(&str, Box<dyn Fn(f64, f64) -> f64>)> = vec![
        ("plus", Box::new(|a, b| a + b)),
        ("mal", Box::new(|a, b| a * b)),
    ];
    for (name, f) in &operationen { print!("{name}={} ", f(6.0, 7.0)); }
    println!();
}

Ausgabe:

15 18
101
plus=13 mal=42

Rekursion und Konstanten

fn fakultaet(n: u64) -> u64 { if n <= 1 { 1 } else { n * fakultaet(n - 1) } }
const fn quadrat(x: u32) -> u32 { x * x }        // zur Übersetzungszeit auswertbar
const NEUN: u32 = quadrat(3);
 
fn main() {
    println!("{} {} {}", fakultaet(10), fakultaet(20), NEUN);
}

Ausgabe:

3628800 2432902008176640000 9

Merke

  • fn name(param: Typ) -> Rückgabetyp { ... }; der letzte Ausdruck ohne ; ist der Rückgabewert
  • return für frühes Verlassen; Tupel liefern mehrere Werte
  • Closures |x| x + 1 fangen Variablen ein; move übergibt den Besitz
  • Funktionen und Closures sind Werte: fn(i32) -> i32, impl Fn(...), Box<dyn Fn(...)>
  • const fn rechnet zur Übersetzungszeit

Übungsaufgabe

Schreibe eine Funktion kompose(f, g), die zwei i32-Funktionen zu einer neuen verkettet, und teste sie mit zwei Closures.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Bedingungen, Schleifen und match

Weiter: Ownership, Borrowing und Lebensdauern

Alle Kapitel: Rust im Überblick