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

  1. Match on read_line; on Ok(0) set again=false and break the loop (treat EOF as 'no').
  2. On Err(e), eprintln the error, set again=false, and break to exit cleanly.
  3. Add an EOF guard: if the input string is empty after read_line, break with again=false.
  4. 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

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.