Input Output in Rust Part 2: Branching and Looping

Taking input with Rust, and using it with branching and control flow statements.

Welcome. If you haven’t read the first part, you can read it here. I won’t bore you with the details like why I’m doing this, you can find that out by reading the first part, if you haven’t done that already.

In this part, we will play with branch statements like if-else and match, along with user-entered input. I have not gotten much practice with match statements, so this should be useful. We will also see various control flow statements (mainly loops) with user-entered input.

Using input with branching:

The program below is simple. It basically tells you whether the number you have entered is even or odd. We take a number from the user as an input, and then divide it by two. Then we check whether the remainder is zero or not. If it is zero, then the number is even. Otherwise, the number is odd.

To check the condition with the remainder, the program uses the if-else statement. If the remainder is zero, then the condition is true, and The number is even” gets printed. Otherwise the condition is false, and “The number is odd” gets printed.

use std::io;
fn main() {
    println!("Enter a number to check whether it is even or odd:");
    let mut input_line = String::new();
    io::stdin()
        .read_line(&mut input_line)
        .expect("Failed to read from stdin");
    let num: i32 = input_line.trim().parse().expect("Input is not an integer");
    if num % 2 == 0 {
        println!("The number is even");
    } else {
        println!("The number is odd");
    }
}

$cargo run

Compiling even_odd v0.1.0 (E:\rust-projects\even_odd) Finished dev [unoptimized + debuginfo] target(s) in 3.22s Running target\debug\even_odd.exe Enter a number to check whether it is even or odd: 199 The number is odd

$cargo run cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.01s Running target\debug\even_odd.exe Enter a number to check whether it is even or odd: 1000 The number is even

So, the program works as intended, as we can see from the results above. Of course, just like we saw in the previous part, if the user enters anything but a number, the program will crash with an error.

Using match:

The program below allows the user to make a language choice. It is similar to the number-based menu you hear on a mobile phone service. Press 1 to get engaged. Press 2 for getting married. Strangle the partner to remarry. (Just a joke, don’t harm your life-partner!)

use std::io;

fn main() {
    println!("Enter 1 for Hindi, 2 for English, and 3 for Japanese:");
    let mut input_line = String::new();
    io::stdin()
        .read_line(&mut input_line)
        .expect("Cannot read the input");
    let num_choice: i32 = input_line.trim().parse().expect("Failed to parse");
    match num_choice {
        1 => println!("You have chosen Hindi."),
        2 => println!("You have chosen English"),
        3 => println!("You have chosen Japanese"),
        _ => println!("Wrong number. Please try again."),
    }
}

$cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.13s Running target\debug\menu_example.exe Enter 1 for Hindi, 2 for English, and 3 for Japanese: 1 You have chosen Hindi.

cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.00s Running target\debug\menu_example.exe Enter 1 for Hindi, 2 for English, and 3 for Japanese: 3 You have chosen Japanese

What happens if we provide a value which is not a number? Let’s find out, in not-so-exciting few lines! (Sorry, watched too much Dragon Ball.)

cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.00s Running target\debug\menu_example.exe Enter 1 for Hindi, 2 for English, and 3 for Japanese: a thread 'main' panicked at 'Failed to parse: ParseIntError { kind: InvalidDigit }', src\main.rs:9:53 note: run with RUST_BACKTRACE=1 environment variable to display a backtrace error: process didn't exit successfully: target\debug\menu_example.exe (exit code: 101)

The program crashed with an error before even reaching the match, which is an expected behavior. If you were to create the similar program in any other language (For example, JavaScript or C (There’s no such thing as C/C++ they are different languages!)) you would have used the switch case statement.

The match has many advantages over switch, but the most important one being you can’t exclude this line:

        _ => println!("Wrong number. Please try again."),

This line cannot be skipped. This line handles things if nothing matches the arms above. Whereas in the switch case, the default case is optional, and can be skipped.

Let’s create some programs with loops:

The program below prints the multiplication table.

use std::io;
fn main() {
    println!("Enter a number to multiply:");
    let mut input_line = String::new();
    io::stdin()
        .read_line(&mut input_line)
        .expect("Failed to read from stdin");
    let num: i32 = input_line.trim().parse().expect("Input is not a number");
    for i in 1..11 {
        println!("{}", num * i);
    }
}

After taking the input of the user as String, we save it in the num variable, not using the variable shadowing this time. the loop runs from 1 to 11, and the numbers are multiplied and printed within the println!() macro.

You can change the loop to this:

    for i in 1..=10

this will make it run only from 1 to 10, instead of 1 to 11, as it did previously.

Let’s create a program to find whether a number is prime or not. This program will require us to use both conditionals and loops to accomplish this task. I’m jumping straight to them, because I really don’t want to talk about programs which do print patterns. Cries I hate those!

There are two variations of this program. First only tells us whether the number entered is prime or not. While the second variation actually generates prime numbers between two given ranges. I’ll show you the first program for now, because my attempt to generate prime numbers has met with less than successful results. Without a further adieu, there’s the program.

use std::io;
fn main() {
    println!("Enter a number to find out whether it is prime or not:");
    let mut num: String = String::new();
    io::stdin()
        .read_line(&mut num)
        .expect("Failed to read line");
    let num: i32 = num.trim().parse().expect("Failed to parse");
    let mut flag: bool = false;
    for i in 2..num {
        if num % i == 0 {
            flag = true;
        }
    }
    if flag == true {
        println!("{} is not a prime", num);
    } else {
        println!("{} is a prime", num);
    }
}

After taking the input from the user and converting it to a number, we need a Boolean flag to find out whether the number is prime or not. We enter the loop, which stops right before the entered number. I did not use the ..= with this program, since we only need to check whether the number is divisible with other numbers or not, starting from two. Because prime numbers are only divisible by 1 and themselves, it is not necessary to check whether the number is visible by 1 or by itself.

Flag has the initial value of false. But if it does turn into true in the loop, we know the number is not prime. Outside of the loop, we test the flag. If true, the number is not prime. If false, the number is prime. Hopefully, you didn’t get too confused. I know I suck at explaining things.

On a side note, I totally forgot about the flag thing. I stubbornly thought that I could write this program without using the Boolean flag. but after I dug up my old c program, which I wrote for an assignment, and looking around the internet, I realize that it is hard, or not even possible. If you can write this program without using Boolean flags in Rust, please do share it in the comments. I’ll be happy to see it.

Here’s how the program performs when it runs:

cargo run Compiling prime_or_not v0.1.0 (E:\rust-projects\prime_or_not) Finished dev [unoptimized + debuginfo] target(s) in 1.66s Running target\debug\prime_or_not.exe Enter a number to find out whether it is prime or not: 97 97 is a prime

cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.00s Running target\debug\prime_or_not.exe Enter a number to find out whether it is prime or not: 91 91 is not a prime

It even works with number 2, for which I initially thought of implementing special logic. But it is not necessary:

cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.00s Running target\debug\prime_or_not.exe Enter a number to find out whether it is prime or not: 2 2 is a prime

Now, next time, I will show you functions, where we will see how can we pass the arguments entered by user, and returning results.

Did you find this article valuable?

Support Tanish Shrivastava by becoming a sponsor. Any amount is appreciated!