coding-horror/basic-computer-games · error

Failed to read line.

Error message

Failed to read line.

What it means

This is the central input utility for Tower of Hanoi: a reusable prompt() function that loops until valid input is received, then returns a PromptResult enum (Number or YesNo). The .expect("Failed to read line.") panics on io::Error. Critically, on EOF (Ok(0)) the function does NOT panic — empty input never matches a valid i32 parse or yes/no arm, so it prints the re-prompt and loops. Because EOF is sticky on stdin, this creates an infinite busy-loop that floods stdout with the prompt message forever. No caller catches the panic because PromptResult has no error variant.

Source

Thrown at 90_Tower/rust/src/util.rs:17

use std::io;

pub enum PromptResult {
    Number(i32),
    YesNo(bool),
}

pub fn prompt(numeric: bool, msg: &str) -> PromptResult {
    use PromptResult::*;
    loop {
        println!("{}", msg);

        let mut input = String::new();

        io::stdin()
            .read_line(&mut input)
            .expect("Failed to read line.");

        let input = input.trim().to_string();

        if numeric {
            if let Ok(n) = input.parse::<i32>() {
                return Number(n);
            }

            println!("PLEASE ENTER A NUMBER.")
        } else {
            match input.to_uppercase().as_str() {
                "YES" | "Y" => return YesNo(true),
                "NO" | "N" => return YesNo(false),
                _ => println!("PLEASE ENTER (Y)ES OR (N)O."),
            }
        }
    }
}

View on GitHub (pinned to 5301155192)

Solutions

  1. Match on read_line; on Ok(0) (EOF) call std::process::exit(0) or return a sentinel to end the infinite loop — this is the most impactful fix since the hang is worse than the panic.
  2. Add a None or Eof variant to PromptResult so callers can react to input exhaustion instead of hanging or crashing.
  3. On Err(e), eprintln the error and process::exit(1) for a clean shutdown with a diagnostic.
  4. For testing, ensure piped input has a line for every prompt the game will issue across all turns.

Example fix

// before
io::stdin()
    .read_line(&mut input)
    .expect("Failed to read line.");

// after
match io::stdin().read_line(&mut input) {
    Ok(0) => std::process::exit(0),
    Ok(_) => {}
    Err(e) => {
        eprintln!("Input error: {e}");
        std::process::exit(1);
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Critical: handle BOTH Err (panic) and Ok(0) (infinite loop)
match io::stdin().read_line(&mut input) {
    Ok(0) => std::process::exit(0),  // EOF: stop the infinite re-prompt
    Ok(_) => { /* safe to trim and parse */ }
    Err(e) => {
        eprintln!("Input error: {e}");
        std::process::exit(1);
    }
}

Type guard

// If refactoring PromptResult to carry an EOF variant:
// pub enum PromptResult {
//     Number(i32),
//     YesNo(bool),
//     Eof,  // new variant for input exhaustion
// }
// Then callers can match on Eof to exit cleanly.

Try / catch

// In the reusable prompt() function — must handle both failure modes
loop {
    println!("{}", msg);
    let mut input = String::new();
    match io::stdin().read_line(&mut input) {
        Ok(n) if n > 0 => { /* proceed with validation */ }
        Ok(0) => std::process::exit(0),  // EOF: prevent infinite loop
        Err(e) => {
            eprintln!("Input error: {e}");
            std::process::exit(1);       // I/O error: prevent panic
        }
    }
    // ... rest of parsing logic ...
}

Prevention

When it happens

Trigger: Running Tower of Hanoi non-interactively with stdin from /dev/null or an exhausted pipe — causes the infinite-loop hang rather than the panic. A broken pipe or terminal driver error — causes the panic. Any of the four call sites (get_disk_count, get_disk_to_move, ask_which_needle, play-again loop) hitting an I/O failure.

Common situations: CI test that pipes disk counts and move sequences without enough lines. Docker container without -it. Automated UI testing that closes stdin early. Script that feeds partial input and then closes.

Related errors


AI-assisted analysis of coding-horror/basic-computer-games@5301155192 (2026-08-13). Data as JSON: /api/errors/379277bbb16b78f2. Report an issue: GitHub.