thecodex.expert · The Codex Family of Knowledge
Go

IO and Files

Two one-method interfaces — io.Reader and io.Writer — are why the same code can read a file, a network socket, or an in-memory buffer without changes.

Go 1.26 io.Reader / io.Writer Last verified:
Canonical Definition

The os package opens, creates, and manipulates files as *os.File values, which themselves implement the tiny io.Reader (one method: Read) and io.Writer (one method: Write) interfaces. Because so many types across the standard library implement these same two interfaces — network connections, in-memory buffers, compressors — code written against io.Reader/io.Writer works unchanged regardless of where the bytes actually come from or go.

Reading a whole file

For small-to-medium files, os.ReadFile reads the entire contents in one call — the simplest option when you don't need to stream.

Goread_file.go
data, err := os.ReadFile("config.txt")
if err != nil {
    log.Fatal(err)
}
fmt.Println(string(data))

Opening a file and using defer to close it

For more control (streaming large files, writing, appending), os.Open/os.Create return an *os.File that must be explicitly closed — idiomatically with defer right after the open succeeds, guaranteeing cleanup regardless of how the function returns.

Goopen_file.go
f, err := os.Open("data.txt")
if err != nil {
    log.Fatal(err)
}
defer f.Close()   // guaranteed to run when this function returns

scanner := bufio.NewScanner(f)
for scanner.Scan() {
    fmt.Println(scanner.Text())   // one line at a time, memory-efficient
}

Writing files

os.WriteFile writes a full byte slice in one call, creating the file if needed; os.Create plus Write gives more control for streaming output.

Gowrite_file.go
err := os.WriteFile("output.txt", []byte("hello\n"), 0644)
if err != nil {
    log.Fatal(err)
}

Sources

1
The Go Authors. io package and os package documentation, pkg.go.dev/io and pkg.go.dev/os.