49. std::io::pipe — Anonymous Pipes in the Standard Library
Need to wire up stdout and stderr from a child process, or stream data between threads? Since Rust 1.87, std::io::pipe() gives you OS-backed anonymous pipes without reaching for external crates.
What’s an anonymous pipe?
A pipe is a one-way data channel: one end writes, the other reads. Before 1.87, you needed the os_pipe crate or platform-specific code to get one. Now it’s a single function call:
| |
pipe() returns a (PipeReader, PipeWriter) pair. PipeReader implements Read, PipeWriter implements Write — they plug into any generic I/O code you already have.
Merge stdout and stderr from a child process
The killer use case: capture both output streams from a subprocess as a single interleaved stream:
| |
The try_clone() on the writer lets both stdout and stderr write to the same pipe. When both copies of the PipeWriter are dropped (one moved into stdout, one into stderr), reads on the PipeReader return EOF.
Why not just use Command::output()?
Command::output() captures stdout and stderr separately into Vec<u8> — you get two blobs, no interleaving, and everything is buffered in memory. With pipes, you can stream the output as it arrives, merge the two streams, or fan data into multiple consumers. Pipes give you the plumbing; output() gives you the convenience.
Key behavior
A read on PipeReader blocks until data is available or all writers are closed. A write on PipeWriter blocks when the OS pipe buffer is full. This is the same behavior as Unix pipes under the hood — because that’s exactly what they are.