Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why don't changes made to a struct via a method persist?

Tags:

methods

struct

go

I'm trying to understand why the following test code is not working as expected:

package main

import (
    "fmt"
    "strings"
)

type Test struct {
    someStrings []string
}

func (this Test) AddString(s string) {
    this.someStrings = append(this.someStrings, s)
    this.Count() // will print "1"
}

func (this Test) Count() {
    fmt.Println(len(this.someStrings))
}

func main() {
    var test Test
    test.AddString("testing")
    test.Count() // will print "0"
}

This would print:

"1"
"0"

Meaning that someStrings is apparently modified... and then it's not.

Anybody know what could be the issue?

like image 519
laurent Avatar asked May 14 '13 10:05

laurent


2 Answers

The AddString method is using a value (copy) receiver. The modification is made to the copy, not the original. A pointer receiver must be used to mutate the original entity:

package main

import (
        "fmt"
)

type Test struct {
        someStrings []string
}

func (t *Test) AddString(s string) {
        t.someStrings = append(t.someStrings, s)
        t.Count() // will print "1"
}

func (t Test) Count() {
        fmt.Println(len(t.someStrings))
}

func main() {
        var test Test
        test.AddString("testing")
        test.Count() // will print "0"
}

Playground


Output

1
1
like image 151
zzzz Avatar answered Nov 08 '22 04:11

zzzz


Your functions are defined on the object themselves rather than a pointer to the object.

func (this Test) AddString(s string) {
    this.someStrings = append(this.someStrings, s)
    this.Count() // will print "1"
}

The function above is defined on the concrete data. This means that when you call the function, the value of this is passed in as a copy of the data. So any mutations you do to this are done on the copy (in this case, the mutation is changing the pointer that 'someStrings' points to. We can rewrite the same function defined on a pointer of Test as jnml did:

func (this *Test) AddString(s string) {
    this.someStrings = append(this.someStrings, s)
    this.Count() // will print "1"
}

As you can see, the function definition is (this *Test) instead of (this Test). This means that the variable this is passed by reference, and any mutations that take place are mutations performed on the original object.

like image 37
noj Avatar answered Nov 08 '22 04:11

noj