A function that reads input need not depend on a file. Accepting an io.Reader lets the same processing code consume a file, standard input, a memory buffer, or another data source. The interface specifies reading behavior rather than where the bytes come from. section 10.3
Listing 2.40 Excerpt from go doc io.Reader
type Reader interface {
Read(p []byte) (n int, err error)
}
Reader is the interface that wraps the basic Read method.
Read reads up to len(p) bytes into p. It returns the number of bytes
read (0 <= n <= len(p)) and any error encountered. Even if Read returns
n < len(p), it may use all of p as scratch space during the call. If
some data is available but not len(p) bytes, Read conventionally returns
what is available instead of waiting for more.
The interface has one method: Read fills a supplied byte slice with up to len(p) bytes and returns the number read and any error. A read can return fewer bytes than the buffer holds. section 2.8.2
Count words in a file
os.Open returns an *os.File, whose Read method satisfies io.Reader. Pass it to bufio.NewScanner, then check both the open error and the scanner error: func countWords(path string) (int, error) { file, err := os.Open(path) if err != nil { return 0, err } defer file.Close() scanner := bufio.NewScanner(file) count := 0 for scanner.Scan() { count += len(strings.Fields(scanner.Text())) } return count, scanner.Err() } section 2.8.2 section 2.8.1
Use io.Reader at an input boundary when processing should not care which source supplies the bytes. Pair it with io.Writer for destination-independent output; use a combined interface such as io.ReadCloser when the caller also needs closing behavior. section 10.3


