Loops

C++

For loops

A for loop in C/C++ consists of 3 expression sections housed in the for() section and a block of code to execute:

The three segments of a for statement allow:

  • Zero or more variables to be initialized (can be empty)
  • Zero or more conditions to be true for the loop to continue (can be empty)
  • Zero or more actions to perform on each iteration (can be empty).

So this is a valid for loop:

  1. // Infinite
  2. for (;;) {
  3. //...
  4. }

So is this:

  1. for (int i = 10, j = 0; (j = i * i) <= 100; i--) {
  2. //...
  3. }

This is clearly a convoluted and somewhat confusing loop because it mixes assignment and conditional tests into the terminating text, but it is one which is entirely legal.

Iterating a range

A C++ loop consists of an initialising expression, a condition expression and a a loop expression separated by semicolons. So a loop that iterates from 0 to 100 looks like this:

  1. for (int i = 0; i < 100; i++ ) {
  2. cout << "Number " << i << endl;
  3. }

Iterating C++ collections

C++ introduces the concept of iterators to its collection classes. An iterator is something that can increment or decrement to traverse a collection.

So to iterate a collection from one end to the other, an iterator is assigned with the collection’s begin() iterator and incremented until it matches the end() iterator.

  1. for (std::vector<string>::const_iterator i = my_list.begin(); i != my_list.end(); ++i ) {
  2. cout << "Value = " << *i << end;
  3. }

C++11 provides new range based for-loop with simpler syntax when iterating over arrays and collections:

  1. std::vector values;
  2. ...
  3. for (const auto & v: values) {
  4. ...
  5. }
  6. int x[5] = { 1, 2, 3, 4, 5 };
  7. for (int y : x) {
  8. ...
  9. }

Infinite Loop

An infinite loop is one that never ends. The typical way to do this in C++ is to test against an expression that always evaluates to true or use an empty for loop:

  1. while (true) {
  2. poll();
  3. do_work();
  4. }
  5. // Or with a for loop
  6. for (;;) {
  7. poll();
  8. do_work();
  9. }

While Loop

C++ has conditional while() {} and do { } while() forms. The former tests the expression before it even runs while the latter runs at least once before testing the expression.

  1. while (!end) {
  2. std::string next = getLine();
  3. end = next == "END";
  4. }

The do-while form in C++ will execute the loop body at least once because the condition is only tested after each iteration instead of before.

  1. int i = 0;
  2. do {
  3. i = rand();
  4. } while (i < 20);

Break and Continue

If you need to exit a loop or start the next iteration early then you use the break and continue keywords. The break keyword terminates the loop, the continue, causes the loop to proceed to the next iteration.

  1. bool foundAdministrator = false;
  2. for (int i = 0; i < loginCredentials; ++i) {
  3. const LoginCredentials credentials = fetchLoginAt(i);
  4. if (credentials.disabled) {
  5. // This user login is disabled so skip it
  6. continue;
  7. }
  8. if (credentials .isAdmin) {
  9. // This user is an administrator so no need to search rest of list
  10. foundAdministrator = true;
  11. break;
  12. }
  13. // ...
  14. }

Rust

For loop

Rust’s for loop is actually sugar over the top of iterators. If a structured type implements the trait IntoIterator it can be looped over using a for loop.

Basically in pseudo code, the loop desugars to this:

  1. If structure type can be turned `IntoIterator`
  2. Loop
  3. If let Some(item) = iterator.next() {
  4. do_action_to_item(item)
  5. Else
  6. break;
  7. End
  8. Else
  9. Compile Error
  10. Done

Iterating a range

A Range object in Rust is expressed as from..to where from and to are values or expressions that evaluate to values.

For example:

  1. let range=0..33;
  2. // Variables
  3. let min = 0;
  4. let max = 100;
  5. let range2 = min..max;

A range is inclusive / exclusive, i.e. the minimum value is included in the Range but the maximum value is exclusive.

Here is a simple loop that counts from 0 to 9

  1. for i in 0..10 {
  2. println!("Number {}", i);
  3. }

The value 0..10 is a Range that runs from 0 to exclusive of 10. A range implements the Iterator trait so the for loop advances one element at a time until it reaches the end.

Iterators have a lot of functions on them for doing fancy stuff, but one which is useful in loops is the enumerate() function. This transforms the iterator into returning a tuple containing the index and the value instead of just the value.

So for example:

  1. for (i, x) in (30..50).enumerate() {
  2. println!("Index {} is value {}", i, x);
  3. }

For loop - Iterating arrays and collections

Here is a loop that iterates an array:

  1. let values = [2, 4, 6, 7, 8, 11, 33, 111];
  2. for v in &values {
  3. println!("v = {}", v);
  4. }

Note you can only iterate over an array by reference because iterating it by value would be destructive.

We can directly use the iter() function that arrays and collections implement which works by reference:

  1. let values = vec![2, 4, 6, 7, 8, 11, 33, 111];
  2. for v in values.iter() {
  3. println!("v = {}", v);
  4. }

If the collection is a map, then iterators will return a key and value tuple

  1. use std::collections::HashMap;
  2. let mut values = HashMap::new();
  3. values.insert("hello", "world");
  4. //...
  5. for (k, v) in &values {
  6. println!("key = {}, value = {}", k, v);
  7. }

Another way to iterate is using the for_each() function on the iterator itself:

  1. let values = [2, 4, 6, 7, 8, 11, 33, 111];
  2. values.iter().for_each(|v| println!("v = {}", v));

Break and Continue

Rust also has break and continue keywords and they operate in a similar fashion - they operate on the innermost loop. A continue will start on the next iteration while a break will terminate the loop.

  1. let values = vec![2, 4, 6, 7, 8, 11, 33, 111];
  2. for v in &values {
  3. if *v % 2 == 0 {
  4. continue;
  5. }
  6. if *v > 20 {
  7. break;
  8. }
  9. println!("v = {}", v);
  10. }

Labels

The break and continue work by default on the current loop. There will be occasions where you intend to break out
of an enclosing loop instead. For those occasions you can label your loops and pass that label into the break or `continue:

  1. 'x: for x in 0..10 {
  2. 'y: for y in 0..10 {
  3. if x == 5 && y == 5 {
  4. break 'x;
  5. }
  6. println!("x = {}, y = {}", x, y);
  7. }
  8. }

Infinite Loop

Rust has an explicit infinite loop that runs indefinitely:

  1. loop {
  2. poll();
  3. do_work();
  4. }

Rust recommends using this form when an infinite loop is required to assist with code generation. Note that an
infinite loop can still be broken out of using a break statement.

While Loop

A while loop in Rust looks pretty similar to one written in C/C++. The main difference is that parentheses are not necessary around the conditional test.

  1. while request_count < 1024 {
  2. process_request();
  3. request_count = request_count + 1;
  4. }

Rust has no equivalent to the do-while loop form. It can be simulated but it looks a bit inelegant:

  1. let mut i = 0;
  2. loop {
  3. i = i + 1;
  4. if i >= 20 { break; }
  5. }

While let loop

Just as there is an if let which tests and assigns a value that matches a pattern, there is also a while let equivalent:

  1. let mut iterator = vec.into_iter();
  2. while let Some(value) = iterator.next() {
  3. process(value);
  4. }

This loop will break when the iterator returns None.