Rust for the Novice Programmer/Basic Maths Testing Program/String to Number

Converting a String to a Number edit

So now we have read a String out of the standard input. But we want to convert it into a number. To do this, we use the following function inside i32:

fn from_str_radix(src: &str, radix: u32) -> Result<i32, ParseIntError>

The src is the string we are converting into a number. The radix is the base of the number we want to convert to. We only want to use base 10 since that's what people will type in. Also the function returns a Result indicating that this function can fail. We could use .unwrap() on the result but this means the program would crash if the user ever typed in anything that couldn't be converted to a number. Instead we want to handle the error properly, and if the user input can't be converted to a number we should ask the user to enter again. Here's what that looks like:

fn wait_for_input() -> i32 {
    let mut buffer = String::new();
    io::stdin().read_line(&mut buffer).unwrap();

    let mut result = i32::from_str_radix(&buffer, 10);

    while let Err(_) = result {
        println!("Invalid number, please re-enter:");
        buffer.clear();
        io::stdin().read_line(&mut buffer).unwrap();
        result = i32::from_str_radix(&buffer, 10);
    }

    let num = result.unwrap();

    num
}

Note that we loop while the result is an error, so the unwrap at the end will always be safe. Also we have to clear the buffer between each go. Now let's test this and see how it works!

What is 35 - 23?
5
Invalid number, please re-enter:
12
Invalid number, please re-enter:
6
Invalid number, please re-enter:

Oh dear. Note that we use Ctrl-C to escape the program when it's in a loop like this. But what's the problem? Why can't it parse the string as a number properly? Well let's add a debug println!() statement to see what the value of buffer is right before it gets parsed:

println!("buffer: {:?}", buffer);

Then when we run it, it prints out:

What is 35 - 23?
10
buffer: "10\r\n"
Invalid number, please re-enter:

What are those characters at the end of the string? Well essentially since we have to press Enter to submit the number, this adds a newline to standard input which is included in the buffer. Another annoying thing is that what the newline is depends on your operating system, generally Windows use "\r\n" while other operating systems use "\n". We want to handle both cases, so we will add a line to replace all of these characters with empty strings, so our function becomes:

fn wait_for_input() -> i32 {
    let mut buffer = String::new();
    io::stdin().read_line(&mut buffer).unwrap();
    buffer = buffer.replace("\r", "").replace("\n", "");

    let mut result = i32::from_str_radix(&buffer, 10);

    while let Err(_) = result {
        println!("Invalid number, please re-enter:");
        buffer.clear();
        io::stdin().read_line(&mut buffer).unwrap();
        buffer = buffer.replace("\r", "").replace("\n", "");
        result = i32::from_str_radix(&buffer, 10);
    }

    let num = result.unwrap();

    num
}

Now our function works, when we run it we get these results:

What is 35 - 23?
test
Invalid number, please re-enter:
1s3r
Invalid number, please re-enter:
10

And the program exits as we expect!

Next: checking whether the user inputted number matches the expected number