🎓 A1Learner
🔍

Rust Broken Pipes

A broken pipe is a pipe whose reader has closed it while the writer still writes. head -3 takes three lines and leaves early.

println! panics on a closed pipe

This loop fills any pipe. Once head leaves, the next write fails. println! turns that failure into a panic:

for i in 1..=100_000 {
    println!("line {i}");
}

Catch it with writeln!

A locked handle plus writeln! gives you the error as a value. Match on its kind. A broken pipe ends the run as a success.

match writeln!(out, "line {i}") {
    Ok(()) => {}
    Err(e) if e.kind() == io::ErrorKind::BrokenPipe => return ExitCode::SUCCESS,
    Err(_) => return ExitCode::FAILURE,
}

Let main return the error

A ? on the write also works. The report is raw, and the exit code is 1. Piped into head -3, the error line is:

let mut out = io::stdout().lock();
for i in 1..=100_000 {
    writeln!(out, "line {i}")?;
}

Example

use std::io::{self, Write};
use std::process::ExitCode;

fn main() -> ExitCode {
    let mut out = io::stdout().lock();
    for i in 1..=100_000 {
        match writeln!(out, "line {i}") {
            Ok(()) => {}
            Err(e) if e.kind() == io::ErrorKind::BrokenPipe => {
                return ExitCode::SUCCESS;
            }
            Err(_) => return ExitCode::FAILURE,
        }
    }
    ExitCode::SUCCESS
}

Key points

  • A broken pipe means the reader closed the pipe early.
  • Rust ignores the SIGPIPE signal at startup.
  • A closed pipe returns an error.
  • It does not kill the program.
  • head, less, and grep -q close pipes on purpose.
  • A broken pipe is a normal ending, not a crash.
  • Treat BrokenPipe as success.
  • The reader took what it wanted.
  • Other write errors are real failures.
  • The panicking version exits with code 101.
  • Scripts see a crash, not a clean finish.

In this example, the run is polite | head -3. head prints the first three lines and closes the pipe. The next writeln! fails with a BrokenPipe error. The match catches that kind and returns ExitCode::SUCCESS. The program exits with code 0, and no panic appears. With no pipe at all, the same program prints all 100,000 lines. Only the ending changes.

Note

In C, the same write ends the program by signal.

The shell reports exit code 141.

Rust hands you the choice instead.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

Why does the println! version panic instead of ending quietly?