Let’s create a simple type, account which has one field money, we will create a one method add which will add some money to the account.

type account struct {
	money int
}

func (a account) add(amount int) {
	a.money += amount
}

Let’s give it a shot

func main() {
	a:= account{}
	fmt.Println("Account before:", a)        // Account before: %v {0}
	a.add(15)
	fmt.Println("Account after add:", a)     // Account after add: %v {0}
}

What just happened? Since by default in Go function arguments are passed by value not by reference, what was passed into the add function was a copy of account. So changes in the value itself doesn’t affect the value of a in the main function. To use more accurate language, that first argument (before the method name) is called function receiver. So, the function receiver is a copy.

There are two ways to go over this, let’s take the more obvious if you are coming from OOP background. Instead of having the receiver of type account we change it to *account.

func (a *account) add(amount int) {
	a.money += amount
}

Without changing anything in the code in main, we will get the result we were looking for.

func main() {
	a:= account{}
	fmt.Println("Account before:", a)        // Account before: %v {0}
	a.add(15)
	fmt.Println("Account after add:", a)     // Account after add: %v {15}
}

Note that conveniently we didn’t need to qualify a as &a (address of a).

Another solution, if you are like me coming from Functional Programming background (Scala), you’d return a copy of the receiver.

func (a account) add(amount int) account {
	a.money += amount
	return a
}

The main function will slightly change:

func main() {
	a := account{}
	fmt.Println("Account before:", a)
	a = a.add(15)
	fmt.Println("Account after add:", a)
}

Another point, you might consider a method as a syntactic sugar of a function where its first argument is the receiver. You can use account.add(a, 1) instead of add on the instance of the type.

In summary, we can think of pointer function receiver as your regular methods in an OOP language, while value function receivers as static methods. In Scala those would be objects within a method. Using the last approach renders types immutable, immutability down side is memory use, but it comes with lots of benefits.

One other thing to note that the receiver type declaration and the method has to be in the same package.


Resources

  1. Ultimate Go Programming
  2. The Go Programming Language
  3. Go Documentation
  4. A tour of Go
  5. Go wiki: Switch
  6. The Go Programming Language Specification
  7. Go by Example