{"record":{"id":"be8dce55403f5454","repo":"coding-horror/basic-computer-games","slug":"failed-reading-line","errorCode":null,"errorMessage":"~~Failed reading line!~~","messagePattern":"~~Failed reading line!~~","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"30_Cube/rust/src/util.rs","lineNumber":31,"sourceCode":"pub fn get_landmines() -> Vec<Position> {\n    let mut landmines = Vec::new();\n\n    for _ in 0..5 {\n        let mut m = get_random_position();\n        while landmines.contains(&m) {\n            m = get_random_position();\n        }\n        landmines.push(m);\n    }\n\n    landmines\n}\n\nfn read_line() -> Result<usize, ParseIntError> {\n    let mut input = String::new();\n    std::io::stdin()\n        .read_line(&mut input)\n        .expect(\"~~Failed reading line!~~\");\n    input.trim().parse::<usize>()\n}\n\npub fn prompt_bool(msg: &str) -> bool {\n    loop {\n        println!(\"{}\", msg);\n\n        if let Ok(n) = read_line() {\n            if n == 1 {\n                return true;\n            } else if n == 0 {\n                return false;\n            }\n        }\n        println!(\"ENTER YES--1 OR NO--0\\n\");\n    }\n}\n","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/coding-horror/basic-computer-games/blob/5301155192d91d74d337899cecc59dbda59c4c17/30_Cube/rust/src/util.rs#L13-L49","documentation":"In 30_Cube, a private read_line() helper calls io::stdin().read_line(...).expect(\"~~Failed reading line!~~\") and then parses the input as usize. The function returns Result<usize, ParseIntError>, so parse failures are already handled gracefully by callers (prompt_bool and prompt_number use if let Ok(n) = read_line()). However, the I/O failure from read_line itself panics, creating an asymmetry: bad input is tolerated but missing input crashes.","triggerScenarios":"The Cube game prompts for a numeric input (boolean 1/0, or a number) and stdin returns Err or EOF. Since callers already loop on parse failures, the only way to reach the panic is an actual I/O-level failure.","commonSituations":"Piping input that ends before the player finishes navigating the cube, pressing Ctrl+D, or running in a non-interactive test where stdin is exhausted.","solutions":["Replace .expect() with ? and change the return type to Result<usize, Box<dyn Error>> to propagate both I/O and parse errors uniformly","Use .unwrap_or(0) so that an I/O failure produces a parse-failing value (0 won't match 1 or 0 in prompt_bool, so the existing retry loop handles it)","Change read_line to return Option<usize> and have callers break the loop on None"],"exampleFix":"// before\nfn read_line() -> Result<usize, ParseIntError> {\n    let mut input = String::new();\n    std::io::stdin()\n        .read_line(&mut input)\n        .expect(\"~~Failed reading line!~~\");\n    input.trim().parse::<usize>()\n}\n\n// after\nfn read_line() -> Option<usize> {\n    let mut input = String::new();\n    if std::io::stdin().read_line(&mut input).ok()? == 0 {\n        return None;\n    }\n    input.trim().parse::<usize>().ok()\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"fn read_line() -> Option<usize> {\n    let mut input = String::new();\n    if std::io::stdin().read_line(&mut input).ok()? == 0 {\n        return None;\n    }\n    input.trim().parse::<usize>().ok()\n}","preventionTips":["When a function returns Result<T, ParseIntError> for parse failures, do not panic on I/O failures — convert I/O errors to the same error path so callers handle them uniformly","Use .ok()? to flatten both I/O and parse errors into Option in helper functions that already have graceful callers","Ensure the error handling strategy is symmetric: if parse failures are tolerated, I/O failures should be too"],"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"}