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));
}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;
}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!();
}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:?}");
}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!();
}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);
}3628800 2432902008176640000 9
Merke
fn name(param: Typ) -> Rückgabetyp { ... }; der letzte Ausdruck ohne;ist der Rückgabewertreturnfür frühes Verlassen; Tupel liefern mehrere Werte- Closures
|x| x + 1fangen Variablen ein;moveübergibt den Besitz - Funktionen und Closures sind Werte:
fn(i32) -> i32,impl Fn(...),Box<dyn Fn(...)> const fnrechnet zur Übersetzungszeit
Aufgabe
Schreibe eine Funktion kompose(f, g), die zwei i32-Funktionen zu einer neuen verkettet, und teste sie mit zwei Closures.