Why would you embed an interface inside a struct? What is the danger?
Question 91HardGo 1.22 to 1.25
Embedding an interface in a struct makes the struct satisfy the interface automatically. You then override only the methods you care about, and all the others delegate to the wrapped value. This is the standard decorator pattern in Go:
// Capture the status code of an http.ResponseWriter
type statusRecorder struct {
http.ResponseWriter // all methods promoted
status int
}
func (r *statusRecorder) WriteHeader(code int) {
r.status = code
r.ResponseWriter.WriteHeader(code)
}
// stdlib example: sort.Reverse
type reverse struct{ sort.Interface }
func (r reverse) Less(i, j int) bool { return r.Interface.Less(j, i) }
// Test fake implementing only what the test needs
type fakeStore struct {
Store // nil; any un-overridden call panics
}
func (fakeStore) Get(id string) (string, error) { return "x", nil }
The dangers:
- If the embedded interface is nil, calling any method you did not override panics with a nil pointer dereference at runtime. The compiler cannot catch it. That is fine for a deliberately partial test fake and dangerous in production.
- The wrapper hides optional interfaces of the inner value.
statusRecorderno longer satisfieshttp.Flusherorhttp.Hijacker, even when the underlying writer did. Go 1.20'shttp.ResponseControllerworks around this when the wrapper has anUnwrap() http.ResponseWritermethod.
More on Interfaces, Methods & Embedding
- Q89What happens when two embedded types provide the same field or method name?
- Q90What is interface embedding, and what changed in Go 1.14 about overlapping methods?
- Q92When should you use
any(interface{}), and when are generics the better choice? - Q93What does
var _ io.Writer = (*MyWriter)(nil)do, and why is it used? - Q94Go interfaces are satisfied implicitly. What design consequences follow, and where should interfaces be defined?
- Q95Explain "accept interfaces, return structs". When is returning an interface justified?