12

My first program in Rust is supposed to take input from the user in the form of characters, either C or F:

use std::io;

fn main() {
    let mut srcunit = String::new();

    let mut switch = true;
    while switch {
        println!("source unit? F or C?");
        io::stdin().read_line(&mut srcunit).expect(
            "failed to read src unit",
        );

        if srcunit.trim() == "F" || srcunit.trim() == "C" {
            println!("doing things right with {}", srcunit);
            switch = false;
        } else {
            println!("either F or C, not {}", srcunit);
        }
    }

    println!("you pressed {}", srcunit);
}

When I start the program and press either F or C, it works right, so I'm skipping this here. The weird part comes when I press another character. I expect my program to ask again for either F or C until I press one of those characters. When I do, it should leave the while loop and tell me what I've pressed.

source unit? F or C?
G
either F or C, not G //so far so good

source unit? F or C?
F
either F or C, not G //why is F not assigned to the srcunit variable? It's supposed to leave the loop now.
F                    //why does it print this line? I didn't press a key or have a println function/macro that does this

source unit? F or C?
V
either F or C, not G //it's still G, wtf
F                    //again, why are those two lines printed? Where does it store, that I pressed F previously?
V
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
fancyPants
  • 50,732
  • 33
  • 89
  • 96

1 Answers1

20

From the documentation for read_line:

Read all bytes until a newline (the 0xA byte) is reached, and append them to the provided buffer.

(Emphasis mine.) You need to clear the string before reading the next line, for example by calling the clear() method on the string, otherwise the answers are accumulated in the variable.

Alternatively, you can define the variable in the loop (but this is slightly less efficient because this way, String will not be able to reuse the already allocated storage for the next answer, it has to be deallocated and reallocated again).

See also this question, asked recently. Looks like this is a common trap.

Florian Weimer
  • 32,022
  • 3
  • 48
  • 92
  • 1
    In other words, those extra lines are printed because they're part of `srcunit`. `read_line` keeps adding the new data to the end of it. – Yann Vernier Jul 21 '17 at 08:47
  • 2
    The key example in [that example](https://doc.rust-lang.org/book/second-edition/ch02-00-guessing-game-tutorial.html#allowing-multiple-guesses-with-looping) (hope I got the link right) is that the variable is declared in the loop, so it is automatically recreated for each iteration. – Florian Weimer Jul 21 '17 at 08:54