Go IO Interfaces
IO interfaces are small contracts for reading and writing streams of bytes. The io.Writer interface asks for one Write method, and io.Reader asks for one Read method.
Writing through an interface
A bytes.Buffer has a Write method, so a pointer to it fits in an io.Writer variable. Fprintf sends text through the variable, and the buffer holds the result.
package main
import (
"bytes"
"fmt"
"io"
)
func main() {
var buf bytes.Buffer
var w io.Writer = &buf
fmt.Fprintf(w, "Hello, %s", "writer")
fmt.Println(buf.String())
}A writer that counts
The counter type gives itself a Write method. The method adds the length of each slice to a stored count.
package main
import "fmt"
type counter struct {
n int
}
func (c *counter) Write(p []byte) (int, error) {
c.n += len(p)
return len(p), nil
}
func main() {
c := &counter{}
n, _ := fmt.Fprintf(c, "count these bytes")
fmt.Println("written:", n)
fmt.Println("counted:", c.n)
}Reading in chunks
A strings.Reader hands out its text a few bytes at a time. Each Read call fills the slice and reports how many bytes it placed.
package main
import (
"fmt"
"io"
"strings"
)
func main() {
var r io.Reader = strings.NewReader("abcdefgh")
buf := make([]byte, 3)
n, _ := r.Read(buf)
fmt.Println(n, string(buf[:n]))
n, _ = r.Read(buf)
fmt.Println(n, string(buf[:n]))
}Example
package main
import (
"bytes"
"fmt"
"io"
"strings"
)
func shout(r io.Reader, w io.Writer) {
data, _ := io.ReadAll(r)
fmt.Fprint(w, strings.ToUpper(string(data)))
}
func main() {
var out bytes.Buffer
shout(strings.NewReader("go is fun"), &out)
fmt.Println(out.String())
}Key points
- An io.Writer value is any value with a Write method that takes bytes in.
- An io.Reader value is any value with a Read method that fills a byte slice.
- A function that takes an io.Writer can write to any type that has a Write method.
- fmt.Fprintf writes formatted text to any io.Writer.
- Read returns the number of bytes it placed in the slice.
In this example, shout takes an io.Reader and an io.Writer. It reads all the text from the reader. It turns the text into upper case with strings.ToUpper. It writes the result to the writer, and main prints what the buffer holds.
Note
A type serves as a writer or a reader by having the right method, with no extra declaration.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Go Playground
Exercise
What makes a type usable as an io.Writer?