pub fn pipe() -> Result<(PipeReader, PipeWriter)>
Expand description
Creates an anonymous pipe.
§Behavior
A pipe is a one-way data channel provided by the OS, which works across processes. A pipe is typically used to communicate between two or more separate processes, as there are better, faster ways to communicate within a single process.
In particular:
- A read on a
PipeReader
blocks until the pipe is non-empty. - A write on a
PipeWriter
blocks when the pipe is full. - When all copies of a
PipeWriter
are closed, a read on the correspondingPipeReader
returns EOF. PipeWriter
can be shared, and multiple processes or threads can write to it at once, but writes (above a target-specific threshold) may have their data interleaved.PipeReader
can be shared, and multiple processes or threads can read it at once. Any given byte will only get consumed by one reader. There are no guarantees about data interleaving.- Portable applications cannot assume any atomicity of messages larger than a single byte.
§Platform-specific behavior
This function currently corresponds to the pipe
function on Unix and the
CreatePipe
function on Windows.
Note that this may change in the future.
§Capacity
Pipe capacity is platform dependent. To quote the Linux man page:
Different implementations have different limits for the pipe capacity. Applications should not rely on a particular capacity: an application should be designed so that a reading process consumes data as soon as it is available, so that a writing process does not remain blocked.
§Example
use std::io::{Read, Write, pipe};
use std::process::Command;
let (ping_reader, mut ping_writer) = pipe()?;
let (mut pong_reader, pong_writer) = pipe()?;
// Spawn a child process that echoes its input.
let mut echo_command = Command::new("cat");
echo_command.stdin(ping_reader);
echo_command.stdout(pong_writer);
let mut echo_child = echo_command.spawn()?;
// Send input to the child process. Note that because we're writing all the input before we
// read any output, this could deadlock if the child's input and output pipe buffers both
// filled up. Those buffers are usually at least a few KB, so "hello" is fine, but for longer
// inputs we'd need to read and write at the same time, e.g. using threads.
ping_writer.write_all(b"hello")?;
// `cat` exits when it reads EOF from stdin, but that can't happen while any ping writer
// remains open. We need to drop our ping writer, or read_to_string will deadlock below.
drop(ping_writer);
// The pong reader can't report EOF while any pong writer remains open. Our Command object is
// holding a pong writer, and again read_to_string will deadlock if we don't drop it.
drop(echo_command);
let mut buf = String::new();
// Block until `cat` closes its stdout (a pong writer).
pong_reader.read_to_string(&mut buf)?;
assert_eq!(&buf, "hello");
// At this point we know `cat` has exited, but we still need to wait to clean up the "zombie".
echo_child.wait()?;