coding-horror/basic-computer-games · error
Failed to read line.
Error message
Failed to read line.
What it means
The play_again function for the Word game loops with a while !valid_response until the user enters a valid Y/N answer. Inside the loop, read_line().expect("Failed to read line.") panics on io::Error. On EOF (Ok(0)) the empty string matches none of the Y/YES/N/NO arms, so the while-loop re-prompts forever — a busy-spin identical to the Tower of Hanoi prompt issue but confined to the play-again gate.
Source
Thrown at 96_Word/rust/src/main.rs:39
game_over = game.tick();
}
quit = !play_again();
}
}
fn play_again() -> bool {
let mut again = true;
let mut valid_response = false;
while valid_response == false {
println!("Want to play again? (Y/n)");
let mut response = String::new();
io::stdin()
.read_line(&mut response)
.expect("Failed to read line.");
match response.trim().to_uppercase().as_str() {
"Y" | "YES" => valid_response = true,
"N" | "NO" => {
again = false;
valid_response = true;
}
_ => (),
}
}
again
}
View on GitHub (pinned to 5301155192)
Solutions
- Match on read_line; on Ok(0) set again=false and break the loop (treat EOF as 'no').
- On Err(e), eprintln the error, set again=false, and break to exit cleanly.
- Add an EOF guard: if the input string is empty after read_line, break with again=false.
- For testing, append a 'n\n' or 'y\n' line after every game round's input.
Example fix
// before
io::stdin()
.read_line(&mut response)
.expect("Failed to read line.");
// after
match io::stdin().read_line(&mut response) {
Ok(0) => { again = false; valid_response = true; }
Ok(_) => {}
Err(e) => {
eprintln!("Input error: {e}");
again = false;
valid_response = true;
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// Break the play-again loop on EOF to prevent infinite spin
match io::stdin().read_line(&mut response) {
Ok(0) => { again = false; valid_response = true; } // EOF: exit
Err(e) => {
eprintln!("Input error: {e}");
again = false;
valid_response = true;
}
Ok(_) => { /* proceed with Y/N match */ }
} Try / catch
// Inside the while !valid_response loop
match io::stdin().read_line(&mut response) {
Ok(n) if n > 0 => {
match response.trim().to_uppercase().as_str() {
"Y" | "YES" => { valid_response = true; }
"N" | "NO" => { again = false; valid_response = true; }
_ => {} // re-prompt
}
}
_ => { again = false; valid_response = true; } // EOF/error: exit
} Prevention
- Break validation loops on EOF — sticky stdin causes infinite re-prompting otherwise.
- Set valid_response=true on EOF to ensure the while-loop terminates.
- Test play-again prompts by piping exactly one game's input and then closing stdin.
- Treat EOF as 'no' (again=false) since a disconnected user cannot continue playing.
When it happens
Trigger: Piped input that closes after a game round ends but before the play-again prompt. Ctrl-D at the 'Want to play again?' prompt (triggers infinite loop, not panic). A broken stdin pipe.
Common situations: Automated testing that pipes one full game's worth of input but no play-again answer. Non-interactive execution with /dev/null stdin (infinite loop). Docker without -it.
Related errors
AI-assisted analysis of coding-horror/basic-computer-games@5301155192 (2026-08-13).
Data as JSON: /api/errors/a27d511129ae2890.
Report an issue: GitHub.