-2

I'm sure there's a good explanation for this, but I've not been able to find it. Can anyone help me understand what is happening the following code example?

package main

import (
    "fmt"
)

type work struct {
    data map[string]string
}

func (w work) doSome() {
    w.data = make(map[string]string)
    w.data["k1"] = "v1"
}

func main() {
    work := work{}
    work.doSome()
    if work.data == nil {
        fmt.Println("data is nil")
    } else {
        fmt.Println("data is", work.data)
    }
}

This prints out data is nil, which is not what I expected. If I rework this to be a pointer type (i.e. *work) for doSome method , it initializes the struct's variable. I'd like to understand why these are different. I assume it's something to do with map being a pointer, of sorts, but haven't been able to find a good reference to explain this.

Playground link - https://play.golang.org/p/lTN11TRkRNj

nbeyer
  • 1,157
  • 10
  • 14
  • I think I found the answer I was looking for here: https://github.com/golang/go/wiki/CodeReviewComments#receiver-type. "If the method needs to mutate the receiver, the receiver must be a pointer." – nbeyer Oct 05 '20 at 19:08

1 Answers1

1

With a value receiver (func (w work) doSome()) the function gets a copy of the work struct. When it sets w.data, it is set for that copy, not for the instance of work declared in main.

Maps are essentially reference types, so if you initialized the map in main, this would've worked:

func (w work) doSome() {
    w.data["k1"] = "v1"
}

func main() {
    work := work{data:map[string]string{}}
    work.doSome()
}

Or, you should use a pointer receiver, so the work declared in main is sent to doSome as a pointer:

func (w *work) doSome() {
    w.data = make(map[string]string)
    w.data["k1"] = "v1"
}

func main() {
    work := work{}
    work.doSome()
}
Burak Serdar
  • 46,455
  • 3
  • 40
  • 59
  • Thanks! The comment about the function getting a copy of the struct is the bit I was looking for! – nbeyer Oct 05 '20 at 19:10