Kontrollfluss

if-Ausdrücke, rekursive Funktionen und Schleifen ebnen uns verschiedene Wege und Abzweigungen auf dem Pfad durch das Programm, die wir in diesem Abschnitt kennenlernen werden. Mit der Nutzung von Funktionen als Parameter haben wir ein weiteres Werkzeug zum Beeinflussen des Kontrollflusses bereits kennengelernt.

if-Ausdrücke

if-Ausdrücke beeinflussen den Programmablauf abhängig von einer Bedingung, die entweder wahr oder falsch ist. Bedingungen haben den Typ bool. Entsprechend gibt der folgende Schnipsel

#![allow(unused)]
fn main() {
let x = 4;
if x > 2 {
    println!("x is larger than 2");
} else {
    println!("x is not larger than 2");
}
}

den Text x is larger than 2 aus, während

#![allow(unused)]
fn main() {
let x = 1;
if x > 2 {
    println!("x is larger than 2");
} else {
    println!("x is not larger than 2");
}
}

zur Textausgabe x is not larger than 2 führt. Es können auch mehrere Bedingungen verwendet werden.

#![allow(unused)]
fn main() {
let x = 1;
if x > 0 {
    println!("x is positive");
} else if x < 0 {
    println!("x is negative");
} else {
    println!("x is 0");
}
}

In Rust geben if-Ausdrücke Werte zurück. Sonst wäre die Bezeichnung Ausdruck schlichtweg falsch.

#![allow(unused)]
fn main() {
let x = 5;
let y = if x < 5 {
    2
} else {
    1
};
assert_eq!(y, 1);
}

Das Macro assert_eq stellt die Gleichheit seiner Argumente sicher. Selbst wenn man nur Anweisungen in if-Ausdrücken verwendet, geben diese das leere Tupel () zurück.

#![allow(unused)]
fn main() {
let x = 6;
let mut y = 0;
if x % 2 == 0 {
    let empty_tupel = if x > 7 {
        y = 1;
    } else {
        y = 2;
    };
    assert_eq!(empty_tupel, ());
    assert_eq!(y, 2);
}
}

Wichtig ist, dass alle Zweige eines if-Ausdrucks den selben Typ zurückgeben. Entsprechend kompilieren folgende Schnipsel nicht.

#![allow(unused)]
fn main() {
// kompiliert nicht
let x = 1;
if x > 1 {
    2  // Gibt eine ganze Zahl zurück.
} else {
    (4, 5)  // Gibt ein Tupel zurück.
}
}
#![allow(unused)]
fn main() {
// kompiliert nicht
let x = 1;
if x > 1 {
    2  // Gibt eine ganze Zahl zurück.
} else {
    let y = 1;
    // Gibt ein leeres Tupel zurück.
}
}
#![allow(unused)]
fn main() {
// kompiliert nicht
let x = 1;
if x > 1 {
    2  // Gibt eine ganze Zahl zurück.
} // else block fehlt und gibt somit ein leeres Tupel zurück.
}

Code Wiederholen

Möchte man einen größeren oder kleineren Haufen Code mehrfach ausführen, helfen Rekursion von Funktionen oder Schleifen. Beispielsweise gibt es verschiedene Möglichkeiten die Fakultät einer ganzen Zahl zu berechnen. Wir beginnen mit der rekursiven Variante.

fn factorial(x: u128) -> u128 {
    if x <= 1 {
        1
    } else {
        // Die Funktion ruft sich selber auf und ist daher rekursiv.
        factorial(x - 1) * x  
    }
}
fn main() {
    println!("{}", factorial(10));
}

Im folgenden wird das gleiche Problem mit einer for-Schleife gelöst.

fn factorial(x: u128) -> u128 {
    let mut res = 1;
    for i in 2..(x+1) {
        res *= i;  // kurz für res = res * i
    }
    res
}
fn main() {
    println!("{}", factorial(10));
}

Die Syntax der for-Schleife beinhaltet ein in, da die for-Schleife insbesondere dafür geeignet ist, über Container wie Arrays zu iterieren. Beispielsweise gibt

#![allow(unused)]
fn main() {
for i in [0, 7, 4] {
    println!("{i}");
} 
}

die Zahlen 0, 7 und 4 aus. Wenn man aber keinen Array hat, sondern über bestimmte Zahlen iterieren möchte, stellt die Rust Standardbibliothek einen Typen namens Range bereit. Beispielsweise wird eine Range von 0 bis 9 durch 0..10 erstellt. Die Zeile for i in 2..(x+1) sorgt also dafür, dass i alle ganzzahligen Werte Zwischen 2 und x annimmt. Dabei hält der Typ Range die Zahlen nicht im Speicher vor. Eine weitere Möglichkeit, die Fakultät einer Zahl zu berechnen, ist die while-Schleife.

fn factorial(x: u128) -> u128 {
    let mut i = x;
    let mut res = 1;
    while i > 1 {
        res = res * i;
        i -= 1;
    }
    res
}
fn main() {
    println!("{}", factorial(10));
}

Die while-Schleife iteriert solange ihre Bedingung wahr ist, während man while true in Rust auch durch loop abbkürzen kann. Schleifen kann man mit dem Schlüsselwort break abbrechen.