{"record":{"id":"3742179e1a6cf286","repo":"coding-horror/basic-computer-games","slug":"failed-to-read-line-374217","errorCode":null,"errorMessage":"Failed to read line","messagePattern":"Failed to read line","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"82_Stars/rust_JWB/src/main.rs","lineNumber":71,"sourceCode":"    print_header();\n    if !read_lowercase_input()?.starts_with('n') {\n        print_rules();\n    }\n    loop {\n        let secret_number : u8 = rand::thread_rng().gen_range(1..101);\n        let mut guess_count = 0;\n        let mut player_won: bool = false;\n        \n        println!(\"\\n\\nOK, I am thinking of a number, start guessing.\");\n        while guess_count < MAX_GUESSES && !player_won {\n            \n            guess_count += 1;        \n\n            println!(\"Your guess? \");\n            let mut guess = String::new();\n            io::stdin()\n                .read_line(&mut guess)\n                .expect(\"Failed to read line\");\n\n            let guess: u8 = match guess.trim().parse() {\n                Ok(num) => num,\n                Err(_) => continue,\n            };\n            \n            // USE THIS STATEMENT FOR DEBUG PURPOSES\n            // println!(\"Guess #{} is {}. secret number is {}\",guess_count, guess, secret_number);\n            \n            if guess == secret_number {\n                // winner winner chicken dinner\n                player_won = true;\n                println!(\"**************************************************!!!\");\n                println!(\"You got it in {guess_count} guesses!!!\");\n            } else {\n                print_stars( guess, secret_number) ;\n            }      \n        }","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/coding-horror/basic-computer-games/blob/5301155192d91d74d337899cecc59dbda59c4c17/82_Stars/rust_JWB/src/main.rs#L53-L89","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","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.","For automated testing, pipe enough lines for every guess plus the game-over prompt, or use a mock stdin crate.","Propagate the error up by changing the loop body to return Result and using the ? operator so callers decide whether to retry or abort."],"exampleFix":"// before\nio::stdin()\n    .read_line(&mut guess)\n    .expect(\"Failed to read line\");\n\n// after\nmatch io::stdin().read_line(&mut guess) {\n    Ok(0) => { println!(\"\\nInput closed. Goodbye!\"); break; }\n    Ok(_) => {}\n    Err(e) => { eprintln!(\"Read error: {e}\"); break; }\n}","handlingStrategy":"try-catch","validationCode":"// Check read_line result before parsing the guess\nlet bytes_read = io::stdin().read_line(&mut guess);\nmatch bytes_read {\n    Ok(0) => { println!(\"Input closed.\"); break; }\n    Err(e) => { eprintln!(\"I/O error: {e}\"); break; }\n    Ok(_) => { /* safe to parse */ }\n}","typeGuard":null,"tryCatchPattern":"// Rust equivalent of try-catch for io::Result\nmatch io::stdin().read_line(&mut guess) {\n    Ok(n) if n > 0 => { /* have data, parse it */ }\n    Ok(0) => break,          // EOF: exit the guess loop\n    Err(e) => {\n        eprintln!(\"Failed to read from stdin: {e}\");\n        break;               // error: exit the guess loop\n    }\n}","preventionTips":["Never call .expect() or .unwrap() on read_line — always match the Result.","Always handle Ok(0) explicitly: EOF is distinct from error and is the most common non-interactive scenario.","Test interactive programs by piping input and verifying graceful exit on exhaustion.","Consider std::process::exit(0) for clean EOF shutdown instead of breaking from nested loops."],"tags":["rust","io","stdin","panic","game-loop"],"backgroundTag":null,"analyzedSha":"5301155192d91d74d337899cecc59dbda59c4c17","analyzedAt":"2026-08-13T19:29:00.979Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}