In Go HTTP handlers, why is the ResponseWriter a value but the Request a pointer?
As correctly mentioned in many other answers here and elsewhere, ResponseWriter
is an interface and the implications of this have been described in detail in SO answers and blogs.
What I would like to address is what I feel is the big—and dangerous—misconception here, that the reason request is passed by "reference" (although such a thing does not really exist in Go) is that "we want to make changes to it visible to the server".
Quoting a couple of answers:
[..] it's just a struct, and since we want to change this struct and have the web server see those changes, it has to be a pointer [..] SO
[..] changes to Request by the handler need to be visible to the server, so we’re only passing it by reference instead of by value [..] SO
This is wrong; in fact the docs explicitly warn against tampering with / mutating the request:
Except for reading the body, handlers should not modify the provided Request.
Quite the opposite, no? :-)
If you want to change the request, e.g. append a tracing header before passing it on to the next handler in a middleware chain you have to copy the request and pass the copied version down the chain.
Requests to change the behavior to allow modifications of the incoming request have been raised with the Go team but changing something like this would probably lead to at least some existing code breaking unexpectedly.
Why use a pointer if we are explicitly telling people not to mutate the request? Performance, Request
is a large struct and copying it can bring performance down, especially with long middleware chains in mind. The team had to strike a balance, definitely not an ideal solution, but the tradeoffs are clearly on the side of performance here (instead of API safety).
What you get for w
is a pointer to the non exported type http.response
but as ResponseWriter
is an interface, that's not visible.
From server.go:
type ResponseWriter interface {
...
}
On the other hand, r
is a pointer to a concrete struct, hence the need to pass a reference explicitly.
From request.go:
type Request struct {
...
}
The http.ResponseWriter
is an interface, and the existing types implementing this interface are pointers. That means there's no need to use a pointer to this interface, as it's already "backed" by a pointer. This concept is describe a bit by one of the go develpers here Although a type implementing http.ResponseWriter didn't need to be a pointer, it would not be practical, at least not within the go http server.
http.Request
is not an interface, it's just a struct, and since we want to change this struct and have the web server see those changes, it has to be a pointer. If it was just a struct value, we would just modify a copy of it that the web server calling our code could not see.