2

I am learning Rust from The Book and I just finished the first exercise, the guessing game.

I use cargo to build and run my little crate.

$ cargo --version
cargo 1.37.0 (9edd08916 2019-08-02)
$ rustc --version
rustc 1.37.0 (eae3437df 2019-08-13)

Everything run fine, including release mode. Nevertheless, I do not understand a the following behaviour of Rust: I have to redeclare the variable which contains the user input at each iteration of the loop.

Since the exercise is guided step-by-step, my code is the same that the one from the book. Nevertheless, the code from the book is the following:

loop {
    // Some code to display instructions.

    // Reallocate a new string at each iteration!
    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,
    };

    // Some code to check if the player found the secret number.
}

Noticing this systematic reallocation, I moved the string declaration outside the loop:

// Allocate the string once.
let mut guess = String::new();

loop {
    // Some code to display instructions.

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

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

    // Some code to check if the player found the secret number.
}

However, Rust did not appreciate this: at the second iteration of the loop, it panics every time.

Why can't I reuse the same mutable variable more than once? Do I not understand something?

EDIT: read_line does not clear the content of the previous input, but appends to it the following one. Let's say the player enters 1 then 2, the final value of guess will be "1\n2\n". However, trim() removes the "blank" characters at the beginning and the end of the string, leaving a \n in the middle: parse() panics!

1 Answers1

5

Your code as-is compiles and runs fine on my setup (same version of rust). The panic must happen in the commented-out part of your code. Some comments, though: the scoping in your loop is tricky: guess at the top-half of the loop is the string declared outside the loop, and is the parsed integer in the second half.

More importantly, multiple calls to read_line being passed the same string appends to the string, which probably isn't your intention given the way you're parsing the string. Sprinkling in println!'s of your guess variables should be illuminating. Your code will probably be fixed if you add a guess.clear() on the string after you've parsed the number, but to do that, you'll probably want to rename the u32 guess.

As an aside, you might consider using a BufReader and the for line in reader.lines()) pattern described here.

Dylan McNamee
  • 1,696
  • 14
  • 16
  • You are absolutely right, ```read_line``` appends the input to the previous string; the debugger showed me that at the second round - let's say, ```1``` then ```2``` - ```guess``` contains ```"1\n2\n"```. ```guess.clear()``` just before the input solves the problem! Thank you! – François de Mareschal Sep 11 '19 at 23:51
  • I edited my answer to include the fix: call guess.clear() before each read_line. – Dylan McNamee Sep 11 '19 at 23:52