Skip to main content
Package io provides basic interfaces to I/O primitives. Its primary job is to wrap existing implementations of such primitives into shared public interfaces that abstract the functionality.

Core Interfaces

Reader

Reader
interface
The interface that wraps the basic Read method.
type Reader interface {
    Read(p []byte) (n int, err error)
}
Read reads up to len(p) bytes into p. It returns the number of bytes read (0 ≤ n ≤ len(p)) and any error encountered. At end of file, Read returns 0, EOF.

Writer

Writer
interface
The interface that wraps the basic Write method.
type Writer interface {
    Write(p []byte) (n int, err error)
}
Write writes len(p) bytes from p to the underlying data stream. It returns the number of bytes written and any error encountered.

Closer

Closer
interface
The interface that wraps the basic Close method.
type Closer interface {
    Close() error
}
The behavior of Close after the first call is undefined.

Seeker

Seeker
interface
The interface that wraps the basic Seek method.
type Seeker interface {
    Seek(offset int64, whence int) (int64, error)
}
Seek sets the offset for the next Read or Write. Returns the new offset or an error.

Composite Interfaces

ReadWriter
interface
Groups the basic Read and Write methods.
type ReadWriter interface {
    Reader
    Writer
}
ReadCloser
interface
Groups the basic Read and Close methods.
type ReadCloser interface {
    Reader
    Closer
}
WriteCloser
interface
Groups the basic Write and Close methods.
type WriteCloser interface {
    Writer
    Closer
}
ReadWriteCloser
interface
Groups the basic Read, Write, and Close methods.
type ReadWriteCloser interface {
    Reader
    Writer
    Closer
}
ReadSeeker
interface
Groups the basic Read and Seek methods.
WriteSeeker
interface
Groups the basic Write and Seek methods.
ReadWriteSeeker
interface
Groups the basic Read, Write, and Seek methods.

Utility Functions

Copying Data

Copy
func Copy(dst Writer, src Reader) (written int64, err error)
Copies from src to dst until either EOF is reached on src or an error occurs. Returns the number of bytes copied and the first error encountered.
written, err := io.Copy(dst, src)
CopyN
func CopyN(dst Writer, src Reader, n int64) (written int64, err error)
Copies n bytes from src to dst. Returns the number of bytes copied and the first error encountered.
// Copy first 100 bytes
written, err := io.CopyN(dst, src, 100)
CopyBuffer
func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error)
Identical to Copy but uses the provided buffer instead of allocating a temporary one.

Reading Data

ReadAll
func ReadAll(r Reader) ([]byte, error)
Reads from r until an error or EOF and returns the data read.
data, err := io.ReadAll(resp.Body)
if err != nil {
    log.Fatal(err)
}
ReadFull
func ReadFull(r Reader, buf []byte) (n int, err error)
Reads exactly len(buf) bytes from r into buf. Returns the number of bytes copied and an error if fewer bytes were read.
buf := make([]byte, 100)
n, err := io.ReadFull(r, buf)
ReadAtLeast
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error)
Reads from r into buf until it has read at least min bytes. Returns the number of bytes copied and an error if fewer bytes were read.
buf := make([]byte, 100)
n, err := io.ReadAtLeast(r, buf, 50)  // Read at least 50 bytes

Writing Data

WriteString
func WriteString(w Writer, s string) (n int, err error)
Writes the contents of string s to w. If w implements a WriteString method, it is invoked directly.
n, err := io.WriteString(w, "Hello, World!")

Helper Types

LimitReader

LimitReader
func LimitReader(r Reader, n int64) Reader
Returns a Reader that reads from r but stops with EOF after n bytes.
// Read only first 1KB
limited := io.LimitReader(file, 1024)
data, err := io.ReadAll(limited)

MultiReader

MultiReader
func MultiReader(readers ...Reader) Reader
Returns a Reader that’s the logical concatenation of the provided readers.
r1 := strings.NewReader("Hello ")
r2 := strings.NewReader("World")
r := io.MultiReader(r1, r2)
io.Copy(os.Stdout, r)  // Hello World

MultiWriter

MultiWriter
func MultiWriter(writers ...Writer) Writer
Creates a writer that duplicates its writes to all provided writers.
// Write to both file and stdout
w := io.MultiWriter(file, os.Stdout)
io.WriteString(w, "Hello")

TeeReader

TeeReader
func TeeReader(r Reader, w Writer) Reader
Returns a Reader that writes to w what it reads from r. All reads from r are written to w.
// Read and log simultaneously
var buf bytes.Buffer
tee := io.TeeReader(resp.Body, &buf)
data, _ := io.ReadAll(tee)
log.Println("Read:", buf.String())

Pipe

Pipe
func Pipe() (*PipeReader, *PipeWriter)
Creates a synchronous in-memory pipe. Reads on the reader block until a write on the writer (or vice versa).
r, w := io.Pipe()

go func() {
    fmt.Fprintln(w, "Hello from pipe")
    w.Close()
}()

io.Copy(os.Stdout, r)

Seek Constants

SeekStart
const int = 0
Seek relative to the origin of the file
SeekCurrent
const int = 1
Seek relative to the current offset
SeekEnd
const int = 2
Seek relative to the end of the file

Common Errors

EOF
var error
Error returned by Read when no more input is available. Functions should return EOF only to signal a graceful end of input.
n, err := reader.Read(buf)
if err == io.EOF {
    // End of input reached
}
ErrUnexpectedEOF
var error
Error indicating EOF was encountered in the middle of reading a fixed-size block or data structure.
ErrShortWrite
var error
Error indicating a write accepted fewer bytes than requested but failed to return an explicit error.
ErrShortBuffer
var error
Error indicating a read required a longer buffer than was provided.

Examples

Reading from a File

package main

import (
    "io"
    "log"
    "os"
)

func main() {
    file, err := os.Open("example.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()
    
    // Read all content
    data, err := io.ReadAll(file)
    if err != nil {
        log.Fatal(err)
    }
    
    log.Printf("Read %d bytes\n", len(data))
}

Copying Files

package main

import (
    "io"
    "log"
    "os"
)

func copyFile(src, dst string) error {
    source, err := os.Open(src)
    if err != nil {
        return err
    }
    defer source.Close()
    
    destination, err := os.Create(dst)
    if err != nil {
        return err
    }
    defer destination.Close()
    
    _, err = io.Copy(destination, source)
    return err
}

func main() {
    err := copyFile("source.txt", "dest.txt")
    if err != nil {
        log.Fatal(err)
    }
}

Using MultiWriter

package main

import (
    "io"
    "log"
    "os"
)

func main() {
    logFile, err := os.Create("app.log")
    if err != nil {
        log.Fatal(err)
    }
    defer logFile.Close()
    
    // Write to both file and stdout
    multi := io.MultiWriter(logFile, os.Stdout)
    
    io.WriteString(multi, "Application started\n")
    io.WriteString(multi, "Processing data\n")
}

Limited Reading

package main

import (
    "io"
    "log"
    "strings"
)

func main() {
    r := strings.NewReader("This is a long string with lots of content")
    
    // Read only first 10 bytes
    limited := io.LimitReader(r, 10)
    
    data, err := io.ReadAll(limited)
    if err != nil {
        log.Fatal(err)
    }
    
    log.Printf("Read: %s\n", data)  // "This is a "
}

Build docs developers (and LLMs) love