coding-horror/basic-computer-games · error

Failed to read line

Error message

Failed to read line

What it means

Rust's std::io::Stdin::read_line reads a line of input into a String and returns io::Result<usize>, where the usize is the number of bytes read (0 means EOF). Calling .expect("Failed to read line") panics only when read_line returns Err(io::Error) — i.e. a genuine I/O failure such as a broken pipe or a bad file descriptor. EOF (Ctrl-D or a closed pipe) returns Ok(0), which does NOT trigger this panic; the empty buffer is then parsed and likely causes a continue or a downstream error instead.

Source

Thrown at 82_Stars/rust_JWB/src/main.rs:71

    print_header();
    if !read_lowercase_input()?.starts_with('n') {
        print_rules();
    }
    loop {
        let secret_number : u8 = rand::thread_rng().gen_range(1..101);
        let mut guess_count = 0;
        let mut player_won: bool = false;
        
        println!("\n\nOK, I am thinking of a number, start guessing.");
        while guess_count < MAX_GUESSES && !player_won {
            
            guess_count += 1;        

            println!("Your guess? ");
            let mut guess = String::new();
            io::stdin()
                .read_line(&mut guess)
                .expect("Failed to read line");

            let guess: u8 = match guess.trim().parse() {
                Ok(num) => num,
                Err(_) => continue,
            };
            
            // USE THIS STATEMENT FOR DEBUG PURPOSES
            // println!("Guess #{} is {}. secret number is {}",guess_count, guess, secret_number);
            
            if guess == secret_number {
                // winner winner chicken dinner
                player_won = true;
                println!("**************************************************!!!");
                println!("You got it in {guess_count} guesses!!!");
            } else {
                print_stars( guess, secret_number) ;
            }      
        }

View on GitHub (pinned to 5301155192)

Solutions

  1. Replace .expect() with a match on read_line's Result: on Ok(0) break the loop (EOF), on Err(e) print a diagnostic and break, on Ok(_) proceed with parsing.
  2. If the program should simply exit on stdin failure, use std::process::exit(0) inside the Err/EOF arm to avoid an ugly panic backtrace.
  3. For automated testing, pipe enough lines for every guess plus the game-over prompt, or use a mock stdin crate.
  4. Propagate the error up by changing the loop body to return Result and using the ? operator so callers decide whether to retry or abort.

Example fix

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

// after
match io::stdin().read_line(&mut guess) {
    Ok(0) => { println!("\nInput closed. Goodbye!"); break; }
    Ok(_) => {}
    Err(e) => { eprintln!("Read error: {e}"); break; }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Check read_line result before parsing the guess
let bytes_read = io::stdin().read_line(&mut guess);
match bytes_read {
    Ok(0) => { println!("Input closed."); break; }
    Err(e) => { eprintln!("I/O error: {e}"); break; }
    Ok(_) => { /* safe to parse */ }
}

Try / catch

// Rust equivalent of try-catch for io::Result
match io::stdin().read_line(&mut guess) {
    Ok(n) if n > 0 => { /* have data, parse it */ }
    Ok(0) => break,          // EOF: exit the guess loop
    Err(e) => {
        eprintln!("Failed to read from stdin: {e}");
        break;               // error: exit the guess loop
    }
}

Prevention

When it happens

Trigger: Piping a limited number of lines into the game (e.g. echo "50\n" | cargo run) and the pipe source closes while the while-guess loop is still iterating. A process supervisor or container that does not allocate a real stdin file descriptor. A terminal driver or SSH session drop mid-read.

Common situations: Running in CI or scripts with piped input that has fewer lines than MAX_GUESSES. Testing the game non-interactively with a here-string. Running under Docker or systemd without -it or StandardInput=tty.

Related errors


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