๐ŸŽ“ A1Learner
๐Ÿ”
0 of 34 lessons completed

Go Methods in Depth

A method set is the group of methods one value can answer. The receiver kind decides which values hold which methods.

A value receiver leaves the original, a pointer receiver changes it

Counter has one method of each receiver kind. Both methods add one, and the prints show which change stays.

package main

import "fmt"

type Counter struct {
	N int
}

func (c Counter) BumpCopy() {
	c.N++
}

func (c *Counter) Bump() {
	c.N++
}

func main() {
	c := Counter{N: 5}
	c.BumpCopy()
	fmt.Println(c.N)
	c.Bump()
	fmt.Println(c.N)
}

An interface holding a pointer answers a pointer method

Speaker asks for a Speak method, and Robot defines Speak on a pointer receiver. An interface holding the pointer can call it.

package main

import "fmt"

type Speaker interface {
	Speak() string
}

type Robot struct {
	Name string
}

func (r *Robot) Speak() string {
	return r.Name + " speaks"
}

func main() {
	var s Speaker = &Robot{Name: "Beep"}
	fmt.Println(s.Speak())
}

An interface holding a value is refused

The same interface now holds a plain Robot value. The value lacks the pointer method, and the compiler refuses the program.

package main

import "fmt"

type Speaker interface {
	Speak() string
}

type Robot struct {
	Name string
}

func (r *Robot) Speak() string {
	return r.Name + " speaks"
}

func main() {
	var s Speaker = Robot{Name: "Beep"}
	fmt.Println(s.Speak())
}

Methods on a named slice type

Names is a named slice type with two methods. The methods read the slice like any other receiver value.

package main

import "fmt"

type Names []string

func (n Names) First() string {
	return n[0]
}

func (n Names) Count() int {
	return len(n)
}

func main() {
	names := Names{"Ana", "Bo", "Cy"}
	fmt.Println(names.First())
	fmt.Println(names.Count())
}

Example

package main

import "fmt"

type Thermostat struct {
	Temp int
}

func (t Thermostat) Read() int {
	return t.Temp
}

func (t *Thermostat) Heat() {
	t.Temp += 2
}

func (t *Thermostat) Cool() {
	t.Temp--
}

func main() {
	th := Thermostat{Temp: 20}
	fmt.Println(th.Read())
	th.Heat()
	fmt.Println(th.Read())
	th.Cool()
	fmt.Println(th.Read())
}

Key points

  • A method with a value receiver receives a copy of the value.
  • A change inside a value receiver method leaves the original unchanged.
  • A method with a pointer receiver receives the pointer, so its changes reach the original.
  • The method set of a pointer includes the methods of both receiver kinds.
  • The method set of a value holds only the value receiver methods.
  • An interface value can call only the methods in the method set of the value it holds.
  • A method can sit on any named type, not only on a struct type.

In this example, the thermostat starts at 20. The Read method has a value receiver, so it reports the current value without changing it. The Heat method has a pointer receiver, and it raises Temp by 2. The next read prints 22. The Cool method lowers Temp by 1, and the last read prints 21.

Note

A pointer value in an interface keeps the full method set, so it answers pointer receiver methods.

Try it Yourself

Edit the code, then press Run.

Running is simulated ยท Go Playground

Exercise

A method must change the struct it is called on. Which receiver should the method use?