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.