Go

What is the exact io.Reader contract? What's wrong with this read loop?

Question 458HardGo 1.22 to 1.25
for {
	n, err := r.Read(buf)
	if err != nil {
		break
	}
	process(buf[:n])
}

The contract: Read may return n > 0 and a non-nil error in the same call (including io.EOF). Callers must process the n bytes before considering the error. The loop above drops the final chunk from readers that return data together with EOF. Also, Read may return fewer bytes than len(buf) even when more data is coming — it doesn't fill the buffer; use io.ReadFull when you need exactly N bytes. Returning 0, nil is discouraged but legal (treat as "nothing happened").

for {
	n, err := r.Read(buf)
	if n > 0 {
		process(buf[:n])
	}
	if err == io.EOF {
		break
	}
	if err != nil {
		return err
	}
}

Better: avoid hand-written loops — use io.Copy, io.ReadAll (bounded with io.LimitReader), bufio.Scanner or io.ReadFull. For io.Writer the contract is stricter: if n < len(p) it must return a non-nil error, and it must not retain p.

Interviewer looks for: "n before err", short reads, and io.EOF being a sentinel value (compare with == or errors.Is).

More on Standard Library, HTTP & Systems Design in Go

All 35 Standard Library, HTTP & Systems Design in Go questions