How can I debug some Rust code to find out why the "if" statement doesn't run?

Viewed 470

This code is what I made from looking at the book "Hands-on Rust", it's basically a copy of the code from the "Searching an Array" part. I don't know why the "if valid" statement doesn't run even though the variable should be set to true.

use std::io::stdin;
fn main() {
    println!("Please enter the key"); //prints the string
    let mut enter = String::new(); //initiates the changeable variable "enter"
    stdin().read_line(&mut enter).expect(r#"invalid key"#); //allows user input and reads the input to assign into the "enter" variable
    enter.trim().to_lowercase(); //trims the "enter" variable out of non-letter inputs and turns them into lowercase
    let key_list = ["1", "2", "3"]; //the input needed to get the desired output
    let mut valid = false; //initiates the "valid" variable to false
    for key in &key_list { //runs the block of code for each item in the "key_list" array
        if key == &enter { //runs the block of code if the variable "enter" matches the contents of the array "key_list"
            valid = true //turns the "valid" variable into true
        }
    };
    if valid { //it will run the block of code if the variable valid is true
        println!("very nice, {}", enter) //it prints the desired output
    } else { //if the if statement does not fulfill the condition, the else statement's block of code will run
        println!("key is either incorrect or the code for this program sucks, your input is {}", enter) //the failure output
    }
}

Please pardon the absurd number of comments if it annoyed you. I did it to try and find where the bad part is.

2 Answers

There's a really cool macro called dbg! I love to use. It's like println! on steroids. You can wrap it around pretty much any variable, expression, or even sub-expression and it'll print the code inside it, the value, and the source location.

Let's add it to the loop and see what's going on:

for key in &key_list {
    if dbg!(key) == dbg!(&enter) {
        valid = true
    }
};

Here's what I see:

Please enter the key
1
[src/main.rs:10] key = "1"
[src/main.rs:10] &enter = "1\n"
[src/main.rs:10] key = "2"
[src/main.rs:10] &enter = "1\n"
[src/main.rs:10] key = "3"
[src/main.rs:10] &enter = "1\n"

Ah! enter didn't actually get trimmed. It's still got the trailing newline. Hm, why is that? Let's take a look at the trim method:

pub fn trim(&self) -> &str

It looks like it returns a new &str slice rather than modifying the input string. We know it can't change it in place because it doesn't take &mut self.

to_lowercase is the same:

pub fn to_lowercase(&self) -> String

The fix:

let enter = enter.trim().to_lowercase();

The problem is that stdin().read_line() returns the input with a trailing newline, but str.trim() and str.to_lowercase() does not mutate the original str. You have to assign it back to enter:

enter = enter.trim().to_lowercase();

You could have spotted it by printing println!("your input is {:?}", enter) with the Debug format specifier, or the other option suggested in John's answer.

Related