coding-horror/basic-computer-games · error

Failed read_line

Error message

Failed read_line

What it means

In 43_Hammurabi, get_input reads a line and parses it as u32, returning Option<u32> — Some on successful parse, None on parse failure. However, the read_line call itself uses .expect("Failed read_line"), so an I/O failure (EOF, broken pipe) panics before the graceful parse-failure path can execute. This creates an asymmetry: non-numeric input returns None (handled), but missing input crashes.

Source

Thrown at 43_Hammurabi/rust/src/main.rs:218

            println!("YOUR PERFORMANCE COULD HAVE BEEN SOMEWHAT BETTER, BUT");
            println!("REALLY WASN'T TOO BAD AT ALL. {haters} PEOPLE");
            println!("WOULD DEARLY LIKE TO SEE YOU ASSASSINATED BUT WE ALL HAVE OUR");
            println!("TRIVIAL PROBLEMS.");
        } else {
            println!("A FANTASTIC PERFORMANCE!!! CHARLEMANGE, DISRAELI, AND");
            println!("JEFFERSON COMBINED COULD NOT HAVE DONE BETTER!\n");
        }
        for _ in 1..10 {
            println!();
        }
    }
            
    println!("\nSO LONG FOR NOW.\n");
}

fn get_input() -> Option<u32> {
    let mut input = String::new();
    io::stdin().read_line(&mut input).expect("Failed read_line");
    match input.trim().parse() {
        Ok(num) => Some(num),
        Err(_) => None,
    }
}

fn gen_random() -> u32 {
    let r: f32 = rand::thread_rng().gen();
    (r * 5.0 + 1.0) as u32
}

fn impossible_task() {
    println!("HAMURABI:  I CANNOT DO WHAT YOU WISH.");
    println!("GET YOURSELF ANOTHER STEWARD!!!!!");
}

fn insufficient_grain(grain: u32) {
    println!("HAMURABI:  THINK AGAIN.  YOU HAVE ONLY");

View on GitHub (pinned to 5301155192)

Solutions

  1. Replace .expect() with .unwrap_or_default() — an empty string fails to parse as u32, so get_input returns None, which callers already handle
  2. Use match on read_line to return None on Ok(0) (EOF) or Err
  3. Change the function to return Result<Option<u32>, io::Error> to separate I/O errors from parse failures

Example fix

// before
fn get_input() -> Option<u32> {
    let mut input = String::new();
    io::stdin().read_line(&mut input).expect("Failed read_line");
    match input.trim().parse() {
        Ok(num) => Some(num),
        Err(_) => None,
    }
}

// after
fn get_input() -> Option<u32> {
    let mut input = String::new();
    if io::stdin().read_line(&mut input).unwrap_or(0) == 0 {
        return None;
    }
    input.trim().parse().ok()
}
Defensive patterns

Strategy: fallback

Try / catch

fn get_input() -> Option<u32> {
    let mut input = String::new();
    if io::stdin().read_line(&mut input).unwrap_or(0) == 0 {
        return None;
    }
    input.trim().parse().ok()
}

Prevention

When it happens

Trigger: The Hammurabi game prompts for a numeric input (bushels to distribute, land to buy, etc.) and stdin is at EOF or the read fails. Non-numeric text like 'abc' correctly returns None, but EOF does not.

Common situations: Piped input that exhausts mid-game (Hammurabi has many sequential prompts per round), pressing Ctrl+D during a steward decision, or CI tests that don't supply all required inputs.

Related errors


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