Schleifen

Rust bietet drei verschiedene Herangehensweisen eine iterative Tätigkeit auszuführen. Es gibt: loop, while und for. Jede dieser Herangehensweisen hat seine eigenen Anwendungsfälle.

loop

Die Endlosschleife loop ist die simpelste Schleifenform, die es in Rust gibt. Mithilfe des loop Schlüsselwortes bietet uns Rust einen Weg an für eine unbestimmte Zeit zu iterieren, bis wir irgendwann eine terminierende Anweisung erreichen.

# #![allow(unused_variables)]
#fn main() {
loop {
    println!("Loop forever!");
}

#}

while

Rust hat auch eine while Schleife. Sie sieht zum Beispiel so aus:

# #![allow(unused_variables)]
#fn main() {
let mut x = 5; // mut x: i32
let mut done = false; // mut done: bool

while !done {
    x += x - 3;

    println!("{}", x);

    if x % 5 == 0 {
        done = true;
    }
}

#}

while Schleifen sind die richtige Wahl, wenn du nicht sicher bist, wie häufig etwas wiederholt werden muss.

Wenn du eine Endlosschleife benötigst, dann bist du vielleicht dazu verleitet das hier zu schreiben:

# #![allow(unused_variables)]
#fn main() {
while true {

#}

Es ist jedoch besser in diesem Fall loop zu verwenden:

# #![allow(unused_variables)]
#fn main() {
loop {

#}

Rusts Kontrollflussanalyse behandelt diese Konstrukt anders als while true, da es weiß, dass die Schleife endlos ist. Allgemein gilt, je mehr Informationen wir dem Compiler geben können, umso bessere Sicherheit und Code-Erzeugung erhalten wir. Deswegen solltest du immer loop vorziehen, falls du planst endlos zu iterieren.

for

Die for Schleife wird benutzt um eine bestimmte Anzahl von Iterationen auszuführen. Rusts for Schleifen arbeiten jedoch ein wenig anders als in anderen Systemsprachen. Rusts for Schleifen sehen nicht aus wie "C-Style" for Schleifen:

for (x = 0; x < 10; x++) {
    printf( "%d\n", x );
}

Stattdessen sehen sie so aus:

# #![allow(unused_variables)]
#fn main() {
for x in 0..10 {
    println!("{}", x); // x: i32
}

#}

In etwas abstrakteren Begriffen:

for var in expression {
    code
}

Der Ausdruck ist ein Iterator. Der Iterator gibt eine Reihe von Elementen zurück. Jedes Element ist eine Iteration der Schleife. Dieses Element wird an den Namen var gebunden, welcher für den Schleifenkörper gültig ist. Sobald der Körper beendet ist, wird der nächste Wert aus dem Iterator geholt und der Schleifenkörper damit erneut ausgefürt. Wenn es keine weiteren Werte mehr gibt, dann ist die for Schleife vorbei.

In unserem Beispiel ist 0..10 ein Ausdruck, welche eine Start- und eine Endposition hat und einen Iterator über diese Werte zurückgibt. Das obere Ende ist jedoch exklusiv, also gibt unsere Schleife nur 0 bis 9, jedoch nicht 10 aus.

Rust hat bewusst keine "C-Style" for Schleifen. Jedes Element der Schleife manuell zu kontrollieren ist kompliziert und fehleranfällig, sogar für erfahrene C-Entwickler.

Enumerate

Wenn du gerne wüsstest wie oft du schon iteriert hast, kannst du die .enumerate() Funktion verwenden.

Bei ranges:

# #![allow(unused_variables)]
#fn main() {
for (i,j) in (5..10).enumerate() {
    println!("i = {} and j = {}", i, j);
}

#}

Ausgabe:

i = 0 and j = 5
i = 1 and j = 6
i = 2 and j = 7
i = 3 and j = 8
i = 4 and j = 9

Vergiss nicht die Klammern um den Range

Bei Iteratoren:

# #![allow(unused_variables)]
#fn main() {
for (linenumber, line) in lines.enumerate() {
    println!("{}: {}", linenumber, line);
}

#}

Ausgabe:

0: Content of line one
1: Content of line two
2: Content of line three
3: Content of line four

Die Iteration frühzeitig beenden

Lass uns einen Blick auf die while Schleife von zuvor werfen:

# #![allow(unused_variables)]
#fn main() {
let mut x = 5;
let mut done = false;

while !done {
    x += x - 3;

    println!("{}", x);

    if x % 5 == 0 {
        done = true;
    }
}

#}

Wir mussten eine boolsche mut Variable done verwenden um die Schleife zu beenden. Rust hat zwei Schüsselwörter, die uns helfen eine Iteration zu modifizieren: break und continue.

In diesem Fall können wir die Schleife mittels break verbessern:

# #![allow(unused_variables)]
#fn main() {
let mut x = 5;

loop {
    x += x - 3;

    println!("{}", x);

    if x % 5 == 0 { break; }
}

#}

Wir iterieren nun endlos und benutzen break um frühzeitig aus der Schleife auszubrechen. Eine explizite return Anweisung würde die Schleife ebenso frühzeitig beenden.

continue ist ähnlich, aber anstatt die Schleife zu beenden, geht man damit zur nächsten Iteration. Das hier wird nur ungerade Zahlen ausgeben:

# #![allow(unused_variables)]
#fn main() {
for x in 0..10 {
    if x % 2 == 0 { continue; }

    println!("{}", x);
}

#}

Schleifen Label

Es könnte sein, dass du in bestimmten Situationen verschachtelte Schleifen hast und festlegen willst für welche dein break oder continue gelten soll. Wir bei den meisten anderen Sprache, gelten break und continue standardmäßig nur für die innerste Schleife. Wenn du allerdings break oder continue auf eine äußere Schleife anwenden möchtest, dann kannst du Label verwenden um das festzulegen. Der folgende Code wird nur etwas ausgeben, wenn sowohl x als auch y ungerade sind.

# #![allow(unused_variables)]
#fn main() {
'outer: for x in 0..10 {
    'inner: for y in 0..10 {
        if x % 2 == 0 { continue 'outer; } // continues the loop over x
        if y % 2 == 0 { continue 'inner; } // continues the loop over y
        println!("x: {}, y: {}", x, y);
    }
}

#}