{"record":{"id":"088cae4651e695b1","repo":"coding-horror/basic-computer-games","slug":"error-reading-line","errorCode":null,"errorMessage":"Error reading line.","messagePattern":"Error reading line\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"29_Craps/rust/src/util.rs","lineNumber":13,"sourceCode":"use std::io;\n\npub enum Response {\n    Yes,\n    No,\n}\n\npub fn read_line() -> String {\n    let mut input = String::new();\n\n    io::stdin()\n        .read_line(&mut input)\n        .expect(\"Error reading line.\");\n\n    input\n}\n\npub fn read_numeric(message: &str) -> usize {\n    loop {\n        println!(\"{}\", message);\n\n        let mut ok = true;\n\n        let input = read_line();\n\n        for c in input.trim().chars() {\n            if !c.is_numeric() {\n                println!(\"You can only enter a number!\");\n                ok = false;\n                break;\n            }","sourceCodeStart":1,"sourceCodeEnd":31,"githubUrl":"https://github.com/coding-horror/basic-computer-games/blob/5301155192d91d74d337899cecc59dbda59c4c17/29_Craps/rust/src/util.rs#L1-L31","documentation":"In 29_Craps, the util module exposes a read_line() helper that wraps io::stdin().read_line(&mut input).expect(\"Error reading line.\"). Every input in the game flows through this function — both read_numeric (for numeric bets) and prompt (for yes/no decisions). The .expect() panics on any stdin I/O failure, crashing the entire game.","triggerScenarios":"Any point in the Craps game where the player must enter input (bet amount, roll-again prompt) and stdin is at EOF or the read syscall fails. Since all input routes through this helper, a single piped-input exhaustion crashes the game.","commonSituations":"Running Craps in CI with piped input that runs out mid-game, a user pressing Ctrl+D during a betting round, or a test harness that closes stdin after the first prompt.","solutions":["Change read_line to return Result<String, io::Error> and use ? in callers, letting the game's main function decide whether to exit or retry","Return Option<String> from read_line (None on EOF/error) and have callers loop or exit on None","Add a loop that retries the read on transient errors but exits on EOF"],"exampleFix":"// before\npub fn read_line() -> String {\n    let mut input = String::new();\n    io::stdin()\n        .read_line(&mut input)\n        .expect(\"Error reading line.\");\n    input\n}\n\n// after\npub fn read_line() -> Option<String> {\n    let mut input = String::new();\n    match io::stdin().read_line(&mut input) {\n        Ok(0) | Err(_) => None,\n        Ok(_) => Some(input),\n    }\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"pub fn read_line() -> Option<String> {\n    let mut input = String::new();\n    match io::stdin().read_line(&mut input) {\n        Ok(0) | Err(_) => None,\n        Ok(_) => Some(input),\n    }\n}","preventionTips":["Centralize all stdin reads in a single helper so I/O error handling is fixed in one place","Return Option or Result from input helpers so callers can decide whether to exit or retry","Never use .expect() in a shared utility function — callers cannot customize the error behavior"],"tags":["rust","stdin","read-line","panic","expect","io-error","eof","interactive","helper","cli"],"backgroundTag":null,"analyzedSha":"5301155192d91d74d337899cecc59dbda59c4c17","analyzedAt":"2026-08-13T19:29:00.979Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}