• English
  • Example: io.Pipe

    Quick Example

    io.Pipe creates a synchronous in-memory connection between a writer and a reader.

    It returns two endpoints:

    • io.PipeReader for reading data;
    • io.PipeWriter for writing data.

    Any data written to the Writer can be read immediately from the Reader.

    It is useful when one part of a program generates data while another part consumes it. By connecting them through io.Pipe, the producer and consumer can run independently without directly depending on each other.

    In the following example, one goroutine writes data into io.PipeWriter, while io.Copy reads data from io.PipeReader and sends it to standard output.

    package main
    
    import (
    	"fmt"
    	"io"
    	"os"
    )
    
    func main() {
    	// 1. Create a pipe.
    	// It returns a reader and a writer connected together.
    	r, w := io.Pipe()
    
    	// 2. Start a separate goroutine to write data.
    	//
    	// io.Pipe has no internal buffer.
    	// Write blocks until another goroutine reads the data.
    	go func() {
    		// Close the writer when no more data will be sent.
    		// Otherwise, the reader waits forever for more data.
    		defer w.Close()
    
    		fmt.Fprint(w, "Hello, io.Pipe from gobase.net.\n")
    	}()
    
    	// 3. Consume data from the reader.
    	// Here we copy the pipe data directly to standard output.
    	_, err := io.Copy(os.Stdout, r)
    
    	if err != nil {
    		fmt.Fprintf(os.Stderr, "reading from pipe failed: %v\n", err)
    		return
    	}
    }

    Output:

    Hello, io.Pipe from gobase.net.

    :::tip io.Pipe has no internal buffer. Its data transfer uses a synchronous handoff:

    • The writer blocks until the reader receives the data.
    • The reader blocks until the writer provides data.

    This behavior makes io.Pipe suitable for streaming scenarios where data should be processed immediately instead of being stored temporarily.

    Important characteristics:

    • Do not perform write-then-read operations in the same goroutine.

      Because there is no internal buffer, the write operation waits for a reader. If both operations happen sequentially in the same goroutine, the program can deadlock.

    • Errors can be passed between goroutines.

      If the producer encounters an error while generating data, it can call:

    •   w.CloseWithError(err)

      The reader will receive this error on subsequent reads after consuming any remaining data. This provides a simple way to propagate errors across goroutines. :::