DEV Community

Cover image for Rust tutorial, manage control flow with if, else-if, else
Chris Noring for Microsoft Azure

Posted on • Edited on

Rust tutorial, manage control flow with if, else-if, else

In this chapter we will cover control flow, how to get your code to conditionally execute as well as be repeated when needed.

Subscribe

Introduction

In this chapter you will:

  • Use if, else and else if to create different execution paths through your code.
  • Apply loops to repeat statements.
  • Add conditions to loops to either break or skip iterations.
  • Iterate collections of data.

Create a new execution path with if.

Let's consider these set of statements.

let salary = 220.2;
let other_income = 10.5;
let tax = 0.2; // percentage

let takehome_salary = (salary + other_income) * (1 as f32 - tax);
Enter fullscreen mode Exit fullscreen mode

Above we have a calculation of someone's salary. Imagine though that someone is exempt from taxes if their income is below a certain income level, what then?

For this case, we can use an if construct to express this condition.

Our code can now be expressed like so:

let no_tax_value = 300.0;
let salary = 220.2;
let other_income = 10.5;
let tax = 0.2; // percentage

let mut takehome_salary = salary + other_income;

if (salary + other_income) > no_tax_value {
  takehome_salary = (salary + other_income) * (1 as f32 - tax); // tax deducted
}
Enter fullscreen mode Exit fullscreen mode

The if clause evaluates an expression (salary + other_income) > no_tax_value that if evaluated to true will run the code within the brackets. If expression evaluates to false then the statement won't be run.

Your program has now two different execution paths depending on the sum of salary + other_income.

Else, what happens if if is false

You can extend your if with an else that runs if if evaluates to false.

In this example a customer is trying to withdraw an amount from their account and it shows an error message if it can't go through with it:

let mut account_balance = 100.0;
let amount = 101.50;
if account_balance - amount > 0 as f32 {
    account_balance -= amount;
} else {
    println!("Can't withdraw {}", amount);
}
Enter fullscreen mode Exit fullscreen mode

From above, the else is placed right next to the if. In fact, it can't exist without the if.

Else if, if you have more than one condition

You've seen if and else so far, but there's one more construct else if that we can use. The idea with else if is to execute if the if evaluates to false. You can have any number of else if. Here's how to use it:

let card_number = 11;
if card_number == 11 {
    println!("Jack");
} else if card_number == 12 {
    println!("Queen");
} else if card_number == 13 {
    println!("King") 
} else if card_number == 14 || card_number == 1 {
    println!("Ace");
} else {
    println!("Card number is {}", card_number);
}
Enter fullscreen mode Exit fullscreen mode

As you can see above, we have a number of else if constructs we can add after the initial if.

if as an expression

Many languages out there has what is called a ternary expression. This means that a value is assigned to a variable in the same row. Here's how it can look in other languages:

// javascript
let salary = 100;
let tax = 0.2;
let take_home_salary = salary > 200 ? salary * (1 as f32 - tax) : salary;
Enter fullscreen mode Exit fullscreen mode

The value lies in not having to define an if with brackets that takes up several rows potentially. However, Rust can do this, like so:

let salary = 100.0;
let tax = 0.2;
let take_home_salary = if salary > 200.0 { salary * (1.0 - tax) } else {salary}; 
println!("Salary is {}", take_home_salary);
Enter fullscreen mode Exit fullscreen mode

Note how the code inside of if and else don't have semicolons ;, which makes them expressions. So here you are using the expression based version of if and else.

Loops

Loops are used when you want to repeat a statement a number of times. That could be for example:

  • command based program, where you ask a user for an input until they choose to quit the program
  • iterating over something:
    • a collection of something, for example "orders" and process each order
    • iterating over files in a directory.

As you can see, there are many cases where looping a set of statements makes sense. Rust offers us many ways to loop.

loop, looping statements

With this construct, you repeat statements forever or until the program is closed down. Here's example code:

loop {
    println!("forever");
} 
Enter fullscreen mode Exit fullscreen mode

Interrupt a loop with break

You can interrupt the looping mechanism though by adding a break to it. Consider the below code for how it would work:

let repeat_times: i32 = 5;
let mut index = 0;
loop {
  if index == repeat_times {
      break;
  }
  println!("Loop {}", index + 1);
  index += 1;

}
Enter fullscreen mode Exit fullscreen mode

This program will print the following:

Loop 1
Loop 2
Loop 3
Loop 4
Loop 5
Enter fullscreen mode Exit fullscreen mode

The preceding code would print "forever" until you quit the program.

Returning from a loop

It's possible to return a value from a loop as you exit it with a break statement.

Here's how you would write that:

let mut points = 0;
let result = loop {
    if points >= 10 {
        break points;
    }
    points += 2;
}
Enter fullscreen mode Exit fullscreen mode

Here, points is incremented for each iteration. Once the break condition is true, the current value of points is returned back break points and the value is assigned to result.

While

You've seen so far how you can use loop and define conditions inside of it and call break to exit the loop. With while, you can define the break condition as part of defining the while, like so:

let mut points = 0;
while points < 10 {
    points += 2;
}
println!("Value of points is {}", points);
Enter fullscreen mode Exit fullscreen mode

Here, your break condition is defined right after the while keyword. There's no need to use an if or break keyword to quit the loop.

Which version is easiest to read, loop or while ?

Iterate a collection

A common reason for wanting to repeat a statement is processing something that has the character of a sequence. It could for example be:

  • a list of points awarded in a swim competition
  • a set of items in shopping basket

To define a list, we can use the following syntax:

let judges_result = [8, 7, 8, 9, 9];
Enter fullscreen mode Exit fullscreen mode

To list a specific items in a list, we use the square brackets, [] and a number between 0 and the length of the list -1. In the above example, 0,1,2,3,4 are addressable indexes. Here's an example:

judges_result[2]; //8 
Enter fullscreen mode Exit fullscreen mode

Trying to access with index 5, would cause an error.

Iteration

So how do we iterate through iterates_result? There are two approaches we could use:

  • while, it's perfectly fine to use a while, however, we need to keep track on when to stop iterating as we can end up out of bounds.
  • for in. With this construct, we are guarenteed to only list items for as long as there are items in the list.

Lets try the first variant with while:

let judges_result = [8, 7, 8, 9, 9];
let mut index = 0;
while index < 5 {
  println!("Judge: {0} is {1} ", index + 1, judges_result[index]);
}
Enter fullscreen mode Exit fullscreen mode

Now, lets compare it to using for in:

let judges_result = [8, 7, 8, 9, 9];
for vote in &judges_result {
  println!("Judge: {} ", vote);
}
Enter fullscreen mode Exit fullscreen mode

Wait, that's not a fair comparison, what happened to the index ?

To get the index, we need to use a slightly more complex construct:

for (i, vote) in judges_result.iter().enumerate() {
     println!("Judge: {0} is {1} ", index + 1, vote);
}
Enter fullscreen mode Exit fullscreen mode

What's happening here is that we call iter(), followed by calling enumerate(). Now we have access to a tuple that contains both the index, i and our value, vote.

REMINDER: a tuple is complex data type created by adding a parenthesis and comma separate values, like so:

  fn create_tuple(a: i32, b: i32) -> (i32, i32) {
      (a, b)
  }
  let (a,b) = create_tuple(2, 4);
  println!("{0} {1}", a, b);
Enter fullscreen mode Exit fullscreen mode

Assignment

Imagine someone read today's entries from a POS (Point of Sale system) from a CSV file into an array entries. Now print out out the total and the average value of a purchase.

INFO: if there's a value in there below 0, it should be thrown a way, as it's an error when reading input.

Here's what running the program can look like:

Here's today's purchases:
33.5, 7.25, 15.5, 44.9
The total is: 101.15
The average purchase was: 25.2875
Enter fullscreen mode Exit fullscreen mode

Solution

fn main () {
    let entries = [33.5, 7.25, 15.5, 44.9, -1.0];
    let mut total:f32 = 0.0;
    let mut approved_entries = 0;
    println!("Today's entries are");
    for item in entries {
        if item < 0 as f32 {
            continue;
        }
        total += item;
        print!("{},", item);
        approved_entries += 1; 
    }
    print!("\n");
    println!("The total is {}", total);
    println!("The average purchase was {}", total / approved_entries as f32);
}
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
liftoffstudios profile image
Liftoff Studios

Nice !!