How does the "optional interface" (interface upgrade) pattern work? Give a stdlib example and its pitfall.
Question 103MediumGo 1.22 to 1.25
A function accepts a small interface, then uses a type assertion to check at runtime whether the value also implements a richer interface. If it does, the function takes a faster or more capable path, and the public API stays minimal. io.Copy is the classic example:
// simplified io.Copy
func Copy(dst Writer, src Reader) (int64, error) {
if wt, ok := src.(WriterTo); ok {
return wt.WriteTo(dst) // e.g. *os.File -> sendfile/splice
}
if rf, ok := dst.(ReaderFrom); ok {
return rf.ReadFrom(src) // e.g. *net.TCPConn
}
return copyBuffer(dst, src, nil) // generic 32 KiB buffer loop
}
// http: optional capabilities of a ResponseWriter
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
Other examples:
io.StringWriter, used byio.WriteString.fmt.Formatter,fmt.Stringeranderror, checked byfmt.json.Marshalerandencoding.TextMarshaler.http.Hijackerandhttp.Pusher.
The pitfall is wrapper types. Middleware that embeds http.ResponseWriter, or a bufio.Writer wrapped around a file, hides the optional methods of the inner value, so the fast path or the feature silently disappears. Server-Sent Events that never flush are a typical symptom. Remedies:
- Implement the optional methods on the wrapper and forward them.
- Expose
Unwrap()forhttp.ResponseController(Go 1.20). - Use libraries that generate every combination of optional interfaces (httpsnoop).
More on Interfaces, Methods & Embedding
- Q101What's the difference between a method value and a method expression? What does this print?
- Q102On which types can you declare methods? Can you add a method to
int,time.Time, a pointer type, or an interface? - Q104How do constraint interfaces in generics differ from ordinary interfaces?
- Q105Do methods carry over with
type B Avstype B = Avs embedding? What does this mean forsync.Mutex? - Q106How does
http.HandlerFuncwork? Explain the function-type adapter pattern. - Q107What happens when you call a value-receiver method through an interface that holds a nil pointer?