coding-horror/basic-computer-games · error

couldn't flush stdout

Error message

couldn't flush stdout

What it means

In 24_Chemist, get_string_from_user_input calls io::stdout().flush() after print! to force the prompt text to appear before read_line blocks. The .expect("couldn't flush stdout") panics if the OS flush syscall returns Err. This is a std::io::Error propagated when stdout's file descriptor is invalid, the downstream pipe consumer has exited, or the terminal device is unavailable.

Source

Thrown at 24_Chemist/rust/src/lib.rs:96

        } else {
            println!(" Good job!  You may breathe now, but don't inhale the fumes!");
            println!();
        }
    }

    //return to main
    Ok(())
}

/// gets a string from user input
fn get_string_from_user_input(prompt: &str) -> Result<String, Box<dyn Error>> {
    //DATA
    let mut raw_input = String::new();

    //print prompt
    print!("{}", prompt);
    //make sure it's printed before getting input
    io::stdout().flush().expect("couldn't flush stdout");

    //read user input from standard input, and store it to raw_input, then return it or an error as needed
    raw_input.clear(); //clear input
    match io::stdin().read_line(&mut raw_input) {
        Ok(_num_bytes_read) => return Ok(String::from(raw_input.trim())),
        Err(err) => return Err(format!("ERROR: CANNOT READ INPUT!: {}", err).into()),
    }
}
/// generic function to get a number from the passed string (user input)
/// pass a min lower  than the max to have minimum and maximum bounds
/// pass a min higher than the max to only have a minimum bound
/// pass a min equal   to  the max to only have a maximum bound
/// 
/// Errors:
/// no number on user input
fn get_number_from_input<T:Display + PartialOrd + FromStr>(prompt: &str, min:T, max:T) -> Result<T, Box<dyn Error>> {
    //DATA
    let raw_input: String;

View on GitHub (pinned to 5301155192)

Solutions

  1. Replace .expect() with the ? operator — the enclosing function already returns Result<String, Box<dyn Error>>, so propagation is zero-cost
  2. Replace print! + flush with print! and let read_line's blocking call naturally flush on most terminal implementations, or use a crate like rustyline that handles prompt display internally
  3. Use writeln!(io::stdout(), "{}", prompt) which writes a newline and is more likely to auto-flush, then trim the trailing newline from input

Example fix

// before
print!("{}", prompt);
io::stdout().flush().expect("couldn't flush stdout");

// after
print!("{}", prompt);
io::stdout().flush()?;
Defensive patterns

Strategy: try-catch

Validate before calling

use std::io::IsTerminal;
if !io::stdout().is_terminal() {
    eprintln!("Warning: stdout is not a terminal; flush failures will be ignored.");
}

Try / catch

match io::stdout().flush() {
    Ok(()) => { /* prompt displayed */ }
    Err(e) if e.kind() == std::io::ErrorKind::BrokenPipe => { /* ignore: downstream consumer exited */ }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Piping the game's stdout through a process that exits before the game finishes (cargo run | head -n 5), redirecting stdout to a closed file descriptor, or running in a sandbox/container where stdout is detached from a valid sink.

Common situations: CI pipelines that pipe game output through pagers or text filters, cargo run | less where the user presses q to quit the pager mid-game, Docker containers launched without a TTY, or automated test harnesses that close stdout on assertion failure.

Related errors


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