Take the following struct:
type CustomResponseWriter struct {
http.ResponseWriter
StatusCode int
}
func (w *CustomResponseWriter) WriteHeader(statusCode int) {
w.ResponseWriter.WriteHeader(statusCode)
w.StatusCode = statusCode
}
Used as:
return http.HandlerFunc(
func(w http.ResponseWriter, request *http.Request) {
response := &CustomResponseWriter{
ResponseWriter: w,
}
next.ServeHTTP(response, request)
logger := NewLogger()
logger.Info(fmt.Sprintf("%d", response.StatusCode))
},
)
This works! StatusCode
gets properly updated!
But how?
ServerHttp
accepts a http.ResponseWriter
, but I’m passing a pointer of a struct implementing http.ResponseWriter
. Actually, it’s not even a proper http.ResponseWriter
because the receiver of WriteHeader
is a pointer and not a value as the original interface defines. These types shouldn’t match, but they do, and it all functions correctly. I don’t quite understand.
«ServerHttp accepts a http.ResponseWriter, but I’m passing a pointer of a struct implementing http.ResponseWriter»–…at which time a value of type http.ResponseWriter is created and populated with that pointer (the interface’s dynamic value) and type information (the interface’s dynamic type). When some other code calls WriteHeader, it’s called on the interface value and gets dispatched to the dynamic value stored in that.
«Actually, it’s not even a proper http.ResponseWriter because the receiver of WriteHeader is a pointer…»–is’t a dubious statement: the receiver of the WriteHeader of a type implementing http.ResponseWriter is a value of that type; if it’s a pointer, it will be a pointer, if it’s not, it will not be a pointer. IOW, if the interface is implemented by
T
, the receivers of its methods have typeT
; if it’s implemented by*T
, the receivers will have type*T
.Regarding interfaces, I highly recommend to read this classic piece: research.swtch.com/interfaces; the only thing incorrect in it (these days) is the statement an interface value can contain dynamic values with the sizes up to platform’s pointer size–this is no longer true since Go 1.4 or 1.5.
You might be also puzzled by why
CustomResponseWriter
implementshttp.ResponseWriter
. That’s because of “embedding” explained here: «A field declared with a type but no explicit field name is called an embedded field. <…> A field or method f of an embedded field in a struct x is called promoted if x.f is a legal selector that denotes that field or method f.»