About Variable Shadowing and Result enum

Hi! I am learning Rust by reading the book. I am not new to the programing. The book is using this code in the linked section:

use rand::Rng;
use std::cmp::Ordering;
use std::io;

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1, 101);

    loop {
        println!("Please input your guess.");

        let mut guess = String::new();

        io::stdin()
            .read_line(&mut guess)
            .expect("Failed to read line");

        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,
        };

        println!("You guessed: {}", guess);

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        }
    }
}

Instead I did use this code by mistake:

use std::io;
use std::cmp::Ordering;

use rand::Rng;

fn main() {
	println!("Guessing game!");

	let secret_number = rand::thread_rng().gen_range(1, 101);
	println!("The secret number is: {}", secret_number);

	let mut guess = String::new();

	loop {
		println!("Please input your guess.");

		io::stdin()
			.read_line(&mut guess)
			.expect("Failed");

		let guess: u32 = match guess.trim().parse() {
			Ok(num) => num,
			Err(_) => continue,
		};

		match guess.cmp(&secret_number) {
			Ordering::Less => println!("Too small!"),
	        Ordering::Greater => println!("Too big!"),
	        Ordering::Equal => {
	        	println!("You win!");
	        	break;
	        }
		}	
	}
}

The second code always selects the Err arm in the match guess.trim().parse() section after first looping as I see. I guess it has to do something with the variable shadowing, which seems interesting and unusual to me currently. Can anybody help me about what is the cause of selecting Err arm in the match? Thanks!

Hi and welcome to the Rust community!

The core issue is that guess is not cleared between loops so after the first loop, it can't be parsed as an integer anymore.

To expand on that, in the first loop your data looks something like this:

Operation Value
stdin().read_line(&mut guess) "1\r"
guess.trim() "1"
guess.trim().parse() Ok(1)

but then guess is not cleared between loops so when the loop continues, you get this

Operation Value
stdin().read_line(&mut guess) "1\r2\r"
guess.trim() "1\r2"
guess.trim().parse() Err(_)
2 Likes

Printing the string shows why this doesn't work. Add dbg!(&guess.trim()); right before let guess: u32 = match guess.trim.parse() { and you'll get the following output that corroborates @wesleywiser's reply:

     Running `target/debug/guess`
Guessing game!
The secret number is: 17
Please input your guess.
10
[src/main.rs:19] &guess.trim() = "10"
Too small!
Please input your guess.
15
[src/main.rs:19] &guess.trim() = "10\n15"
Please input your guess.
32
[src/main.rs:19] &guess.trim() = "10\n15\n32"
Please input your guess.
17
[src/main.rs:19] &guess.trim() = "10\n15\n32\n17"
Please input your guess.

More info about debugging with std::dbg here

1 Like