coding-horror/basic-computer-games · error
Failed to read the line
Error message
Failed to read the line
What it means
This panic is triggered by `.expect("Failed to read the line")` on `io::stdin().read_line()` inside `Question::ask()` in the Literature Quiz game. `read_line` returns `Err` only when the underlying stdin stream encounters an I/O error or is closed/redirected to an invalid source. Because `.expect` is used instead of graceful error handling, any such failure aborts the entire process with this message.
Source
Thrown at 57_Literature_Quiz/rust/src/main.rs:63
question: &'a str,
choices: Vec<&'a str>,
answer: u8,
correct_response: &'a str,
wrong_response: &'a str,
}
impl Question<'_>{
fn ask(&self) -> bool {
println!("{}", self.question);
for i in 0..4 {
print!("{}){}", i+1, self.choices[i]);
if i != 3 { print!(", ")};
}
println!("");
let mut user_input: String = String::new();
io::stdin()
.read_line(&mut user_input)
.expect("Failed to read the line");
if user_input.starts_with(&self.answer.to_string()) {
println!("{}", self.correct_response);
true
} else {
println!("{}", self.wrong_response);
false
}
}
}
let questions: Vec<Question> = vec![
Question{
question: "IN PINOCCHIO, WHAT WAS THE NAME OF THE CAT?",
choices: vec!["TIGGER", "CICERO", "FIGARO", "GUIPETTO"],
answer: 3,
wrong_response: "SORRY...FIGARO WAS HIS NAME.",
correct_response: "VERY GOOD! HERE'S ANOTHER.",View on GitHub (pinned to 5301155192)
Solutions
- Replace `.expect("Failed to read the line")` with `match` on the `Result`, printing a friendly prompt and retrying on `Err` rather than panicking.
- Detect EOF specifically (0 bytes read) and exit the quiz cleanly with a goodbye message instead of crashing.
- If running in CI or a non-interactive context, provide input via a piped file or `expect`-style automation so stdin never closes mid-question.
Example fix
// before
io::stdin()
.read_line(&mut user_input)
.expect("Failed to read the line");
// after
match io::stdin().read_line(&mut user_input) {
Ok(0) => {
println!("\nInput closed. Goodbye!");
std::process::exit(0);
}
Ok(_) => {}
Err(e) => {
eprintln!("Could not read input: {}", e);
std::process::exit(1);
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// Check stdin availability before reading
use std::io::IsTerminal;
if !io::stdin().is_terminal() && std::env::var("FORCE_NONINTERACTIVE").is_err() {
eprintln!("Warning: stdin is not a terminal; input may fail.");
} Type guard
// Rust does not use type guards; instead use Result matching
fn safe_read_line() -> Option<String> {
let mut buf = String::new();
match io::stdin().read_line(&mut buf) {
Ok(0) | Err(_) => None,
Ok(_) => Some(buf),
}
} Try / catch
match io::stdin().read_line(&mut user_input) {
Ok(0) => { println!("Input closed."); std::process::exit(0); }
Ok(_) => { /* proceed */ }
Err(e) => { eprintln!("Read error: {}", e); std::process::exit(1); }
} Prevention
- Never use .expect() on stdin reads in interactive programs — always match on the Result.
- Detect EOF (0 bytes returned) explicitly and exit cleanly rather than crashing.
- When testing non-interactively, pipe input files that cover every prompt the game issues.
When it happens
Trigger: The program's stdin is not a readable TTY — e.g., it is launched in an environment without an attached terminal, stdin is redirected from a closed file descriptor, or an automated test harness pipes EOF (Ctrl+D) into the process. In `Question::ask()`, the call is on the hot path of every quiz question, so the first such stdin disruption panics immediately.
Common situations: Running the binary under CI or a service manager (systemd, Docker with no `-it`) that does not wire up stdin. Piping `/dev/null` or an empty file as input. Pressing Ctrl+D on the terminal instead of typing an answer. Embedding the quiz in a GUI wrapper that forgets to provide stdin.
Related errors
AI-assisted analysis of coding-horror/basic-computer-games@5301155192 (2026-08-13).
Data as JSON: /api/errors/da9eb88a482f4608.
Report an issue: GitHub.