0x_Anakin
0x_Anakin

Reputation: 3269

mut String redeclaration inside loop

I'm reading the Rust book and the guessing game tutorial has the following code:

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

fn main() {
    let secret_number = rand::thread_rng().gen_range(1, 101);
    let mut input = String::new();

    loop {
        print!("Guess the number I'm thinking of: ");
        io::stdout().flush().unwrap();
        io::stdin().read_line(&mut input).expect("Failed to read line");

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

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

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

This code fails after the first user input. If I move the let mut input = String::new(); declaration inside the loop then everything is ok... But I'm wondering why I have to do that since the address of input is passed into read_line()?

And one more thing why do I have to use std::io::Write in order to use io::stdout().flush().unwrap(); since I'm already using use std::io;?

Upvotes: 1

Views: 103

Answers (1)

pretzelhammer
pretzelhammer

Reputation: 15135

io::stdin().read_line(&mut input);

This method doesn't overwrite the String but it appends to the String. This is why only the first iteration of the loop works but it immediately breaks on the second iteration. If you want to re-use the String buffer between iterations you need to truncate it before passing it back into read_line. Fixed working example:

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

fn main() {
    let secret_number = rand::thread_rng().gen_range(1, 101);
    let mut input = String::new();

    loop {
        print!("Guess the number I'm thinking of:");
        io::stdout().flush().unwrap();
        
        input.clear(); // truncate String buffer here!

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

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

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

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

And one more thing why do I have to use std::io::Write in order to use io::stdout().flush().unwrap(); since I'm already using use std::io;?

In order to call trait methods you need to bring the trait into scope. flush is defined by the Write trait.

Upvotes: 2

Related Questions