I noticed some strange behaviour when I create a struct in go that registers an http-Handler in its New
function.
Consider the following code:
package main
import (
"fmt"
"net/http"
)
type Counter struct {
name string
value int
}
func New(name string) Counter {
c := Counter{
name: name,
value: 0,
}
http.HandleFunc("/", c.serve)
return c
}
func (c *Counter) inc() { c.value }
func (c *Counter) reset() { c.value = 0 }
func (c *Counter) nameApp(n string) { c.name = n }
func (c *Counter) print() { fmt.Printf("[%s]: %d (%p)\n", c.name, c.value, &c) }
func (c *Counter) Reinit(name string, value int) {
c.name = name
c.value = value
}
func (c *Counter) serve(w http.ResponseWriter, req *http.Request) {
c.inc()
c.nameApp("-foo")
fmt.Println("Counter served:")
c.print()
w.WriteHeader(http.StatusOK)
w.Write([]byte{})
}
func main() {
c := New("My New Counter")
fmt.Println("New Counter:")
c.print()
c.Reinit("My reinit Counter", 10)
fmt.Println("Counter after Reinit() call:")
c.print()
http.ListenAndServe("localhost:9000", nil)
}
When running it it creates the following output:
New Counter:
[My New Counter]: 0 (0xc00012a2a0)
Counter after Reinit() call:
[My reinit Counter]: 10 (0xc00012a2a0)
After sending two requests to the server the output is:
Counter served:
[My New Counter-foo]: 1 (0xc00012a2c0) // expected "[My reinit Counter]: 11 (0xc00012a2a0)"
Counter served:
[My New Counter-foo-foo]: 2 (0xc00012a2c0) // expected "[My reinit Counter]: 12 (0xc00012a2a0)"
Why does the struct not behave as I had expected, even though I am properly using pointer receivers?
How can I modify my struct from a main routine or really any other routine to and have these changes being reflected in the associated http request handlers?
CodePudding user response:
Found the solution while writing up the question:
The return value of the new function needs to be a pointer, otherwise it will return a copy. The following change solves the problem:
func New(name string) *Counter { // add the *
// ... as before
return &c // add the &
}
(What I don't really get, though, is why the address in initial output stays the same!?)
CodePudding user response:
In
func (c *Counter) print()
You are printing
fmt.Printf("[%s]: %d (%p)\n", c.name, c.value, &c)
Most notably, you are printing &c
via %p
.
c
is the pointer receiver argument to the method. &c
is a pointer to the pointer receiver. In other words, &c
is a pointer to a local variable, the receiver variable. If you want to print just the address of the Counter
which was called on, use the plain c
. For example:
fmt.Printf("[%s]: %d (%p)\n", c.name, c.value, c)