Home > Enterprise >  Why does object state not change when calling methods with a pointer receiver from within a map?
Why does object state not change when calling methods with a pointer receiver from within a map?

Time:11-19

Consider the following code:

package main

import (
    "fmt"
)

type Person struct {
    Age int
    M   map[string]func()
}

func New() Person {
    p := Person{1, make(map[string]func())}
    p.M["1"] = p.Add
    return p
}

func (p *Person) Add() {
    fmt.Println("Age before mutating in Add", p.Age)
    p.Age  = 1 // here age should become 3
    fmt.Println("Age after mutating in Add", p.Age)
}

func (p *Person) Run() {
    fmt.Println("Age before mutating in Run", p.Age)
    p.Age  = 1 // here age should be 2
    p.M["1"]()
    fmt.Println("Age after mutating in Run", p.Age)
}

func main() {
    p := New()
    fmt.Println("age before", p.Age)
    p.Run()
    fmt.Println("age after", p.Age)
}

This produces output

age before 1
Age before mutating in Run 1
Age before mutating in Add 1
Age after mutating in Add 2
Age after mutating in Run 2
age after 2

Here M is a map member of a struct, it stores methods of the same struct against strings.

In main we instantiate a non pointer struct value.

Then we call a method Run on that struct which is defined to have a pointer receiver. This should share a pointer to the struct with the method, which should allow the method to mutate the original struct.

Within Run we call the function Add stored within the map, via the map. But this does not mutate the struct, in fact it seems to operate on the state of the object as it was when newly created.

Any changes within Add are lost by the time Run ends, although Add is also declared with a pointer receiver.

I suspect this is because the original object was copied into the map somehow when calling p.M["1"] = p.Add?

Why does calling the method with pointer receiver not mutate the object?

Playground link https://play.golang.org/p/CRer_rT8_sj

CodePudding user response:

New returns a Person and not a *Person.

When you write

p := New()

it starts with New constructing a Person pnew and adding its Add method to pnew.M. Then it returns pnew.

Then this pnew is assigned to p which is a different variable. So p.M contains now pnew's Add method. When you execute p.M["1"]() in Run pnew's Age is incremented (which is unrelated to p's age.

  • Related