Home > Mobile >  Why does serving a struct via http create a copy?
Why does serving a struct via http create a copy?

Time:07-30

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)
  • Related