09 — io.Reader & io.Writer
Go's streaming I/O: the two tiny interfaces every byte source and sink implements, the helpers that pump between them, and how to write your own. Every example is live — edit it and press Run.
The two interfaces and the helpers
An io.Reader has one method Read; an
io.Writer has Write. Files, sockets,
bytes.Buffer, HTTP bodies — all implement them, so code composes freely.
io.Copy pumps any reader into any writer; bufio.Scanner
tokenises by line; and because Writer is just an interface, your own type
can be one. io.TeeReader mirrors bytes to a writer as they're read.
package main
import (
"bufio"
"bytes"
"fmt"
"io"
"strings"
)
func ReadAll(r io.Reader) (string, error) {
var buf bytes.Buffer
_, err := io.Copy(&buf, r) // pump reader -> buffer
return buf.String(), err
}
func Lines(r io.Reader) ([]string, error) {
sc := bufio.NewScanner(r)
var out []string
for sc.Scan() { // one line per iteration
out = append(out, sc.Text())
}
return out, sc.Err()
}
type CountingWriter struct{ n int }
func (c *CountingWriter) Write(p []byte) (int, error) {
c.n += len(p) // our own io.Writer
return len(p), nil
}
func main() {
s, _ := ReadAll(strings.NewReader("hello world"))
fmt.Println(s)
lines, _ := Lines(strings.NewReader("one\ntwo\nthree"))
fmt.Println(lines)
var cw CountingWriter
io.Copy(&cw, strings.NewReader("hello world"))
fmt.Println(cw.n)
var sink bytes.Buffer
tee := io.TeeReader(strings.NewReader("payload"), &sink)
data, _ := io.ReadAll(tee) // reading tee also fills sink
fmt.Printf("%s %s\n", data, sink.String())
}
Output:
hello world
[one two three]
11
payload payload
End of stream is the value io.EOF, not a magic -1 — though the
helpers above handle it for you. A pointer receiver on Write is needed
because it mutates n, and it's the *CountingWriter that
satisfies io.Writer.
Recap
| Concept | In Go |
|---|---|
| Read from anything | io.Reader — Read(p []byte) |
| Write to anything | io.Writer — Write(p []byte) |
| Pump reader → writer | io.Copy(dst, src) |
| In-memory stream | bytes.Buffer (Reader and Writer) |
| Read lines | bufio.Scanner, check sc.Err() |
| Mirror while reading | io.TeeReader(r, w) |
Next: 10 — Maps & JSON.