coding-horror/basic-computer-games · error
Failed to read line.
Error message
Failed to read line.
What it means
The read_guess method on the WordGame struct calls read_line().expect("Failed to read line.") to read the player's letter guess. The panic fires on io::Error from read_line. On EOF (Ok(0)) the trimmed guess is empty; the validation loop iterates zero characters, so no invalid_input closure fires — the method likely returns false (no valid guess submitted), and the caller skips processing the guess, potentially looping forever depending on the calling game loop structure.
Source
Thrown at 96_Word/rust/src/word_game.rs:52
self.guesses += 1;
println!("\n\nGuess a five letter word?");
let mut game_over = false;
if WordGame::<'_>::read_guess(self) {
game_over = WordGame::<'_>::process_guess(self);
}
game_over
}
fn read_guess(&mut self) -> bool {
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line.");
let invalid_input = |message: &str| {
println!("\n{} Guess again.", message);
return false;
};
let guess = guess.trim();
for c in guess.chars() {
if c.is_numeric() {
return invalid_input("Your guess cannot include numbers.");
}
if !c.is_ascii_alphabetic() {
return invalid_input("Your guess must only include ASCII characters.");
}
}
if guess.len() != 5 {View on GitHub (pinned to 5301155192)
Solutions
- Match on read_line; on Ok(0) return false and signal the caller to end the game.
- On Err(e), eprintln the error and return false so the caller can detect the failure.
- Add an explicit empty-input check after trimming: if guess.is_empty(), return false or break the game loop.
- For testing, ensure piped input covers every guess the game will request.
Example fix
// before
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line.");
// after
match io::stdin().read_line(&mut guess) {
Ok(0) => return false, // EOF: no guess
Ok(_) => {}
Err(e) => {
eprintln!("Input error: {e}");
return false;
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// Return false on EOF/error so the caller skips guess processing
fn read_guess(&mut self) -> bool {
let mut guess = String::new();
match io::stdin().read_line(&mut guess) {
Ok(0) => return false, // EOF: no guess
Err(e) => {
eprintln!("Input error: {e}");
return false;
}
Ok(_) => { /* proceed with validation */ }
}
// ... rest of validation ...
} Try / catch
// In read_guess method — match replaces expect
match io::stdin().read_line(&mut guess) {
Ok(n) if n > 0 => { /* have input, validate characters */ }
Ok(0) => return false, // EOF: signal no valid guess
Err(e) => {
eprintln!("{e}");
return false;
}
} Prevention
- Return false from read_guess on EOF so the caller's game loop can detect disconnection.
- Check for empty guess after trim — an empty string should be rejected, not silently accepted.
- Ensure the calling play loop checks the return of read_guess AND handles repeated false returns (potential infinite loop).
- Test with piped input that exhausts mid-game to verify graceful termination.
When it happens
Trigger: stdin pipe closed during gameplay. A terminal connection lost mid-guess. Non-interactive execution where stdin exhausts before the game ends.
Common situations: Piped input for automated Word game testing that runs out of guesses mid-game. Docker or CI without interactive stdin. Script that provides partial input.
Related errors
AI-assisted analysis of coding-horror/basic-computer-games@5301155192 (2026-08-13).
Data as JSON: /api/errors/8f44284788602d5d.
Report an issue: GitHub.