You can define methods on types.

Method is a function with a special receiver argument.

type Vertex struct {
	X, Y float64
}
 
func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
 
func main() {
	v := Vertex{3, 4}
	fmt.Println(v.Abs())
}

Method can be declared with a receiver type that is defined in the same package as the method.

Non-struct types can be used as well, but not built-ins (as they are not declared in the same package).

type MyFloat float64
 
func (f MyFloat) Abs() float64 {
	if f < 0 {
		return float64(-f)
	}
	return float64(f)
}
 
func main() {
	f := MyFloat(-math.Sqrt2)
	fmt.Println(f.Abs())
}

It is a convention in Go to have the receiver variable be the first letter of the type.

r Rectangle

Pointer receivers

Methods can be declared with pointer receivers:

type Vertex struct {
	X, Y float64
}
 
func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
 
func (v *Vertex) Scale(f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}
 
func main() {
	v := Vertex{3, 4}
	v.Scale(10)
	fmt.Println(v.Abs())
}

They modify the value in place, instead of its copy.

Pointer receivers are more common because methods often need to modify their receiver.

Indirection

Methods with pointer receivers take either a value or a pointer as the receiver when they are called:

func (v *Vertex) Scale(f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}
 
var v Vertex
v.Scale(5)  // OK
p := &v
p.Scale(10) // OK

It’s a convenience that Go provides to us. It interprets v.Scale(5) as (&v).Scale(5) automatically.

It’s not the case with the function without pointer receiver:

func ScaleFunc(v *Vertex, f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}
 
var v Vertex
ScaleFunc(v, 5)  // Compile error!
ScaleFunc(&v, 5) // OK

Similar case is with the value receivers: we can pass either value or a pointer as the receiver.

func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
 
var v Vertex
fmt.Println(v.Abs()) // OK
p := &v
fmt.Println(p.Abs()) // OK
func AbsFunc(v Vertex) float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
 
var v Vertex
fmt.Println(AbsFunc(v))  // OK
fmt.Println(AbsFunc(&v)) // Compile error!

Choose between value or pointer receiver

Reasons to use pointer receiver:

  • You need to modify the value that its receiver points to.
  • Avoid copying the value on each method call. May be important for large structs, for example.

In general, all methods on a given type should have either value or pointer receivers, but not a mixture of both.

q Elaborate on this

https://www.reddit.com/r/golang/comments/10yrpic/why_not_mix_value_and_pointer_receivers/

Whenever that error is encountered, an alternative to changing all of the receivers to pointers is to simply use &S{}. Now the value receivers continue to work fine.

There is no reason to not mix receiver types.

Yeah, I think specifying &S{} tells that “I have this struct, you can mutate it whenever needed”. It feels more intentional than changing receivers to pointer-based for all other methods where it’s not needed.

Important thing to understand is that non-pointer type (e.g. Request) is different than a pointer type (&Request), and they “own” different methods of the interface.

The non-pointer type Request “owns” only one of the two methods, validate(). A pointer type &Request, on the other hand, owns both methods because it is able to follow the pointer receiver in bind() as well as access the non-pointer receiver in validate().

On the other hand this FAQ on the official site makes a point that there may be some race conditions.

func (s *MyStruct) pointerMethod() { } // method on pointer
func (s MyStruct)  valueMethod()   { } // method on value

In the examples above, if pointerMethod modifies the fields of s, the caller will see those changes, but valueMethod is called with a copy of the caller’s argument (that’s the definition of passing a value), so changes it makes will be invisible to the caller.

Second is the consideration of efficiency. If the receiver is large, a big struct for instance, it may be cheaper to use a pointer receiver.

Next is consistency. If some of the methods of the type must have pointer receivers, the rest should too, so the method set is consistent regardless of how the type is used.

By the way, in Java method receivers have always been pointers, although their pointer nature is somewhat disguised (and recent developments are bringing value receivers to Java). It is the value receivers in Go that are unusual.

As the Go specification says, the method set of a type T consists of all methods with receiver type T, while that of the corresponding pointer type *T consists of all methods with receiver *T or T. That means the method set of *T includes that of T, but not the reverse.

Final thought: better to stick with the official recommendation. Default to value receivers. But if there’s a need for a pointer receiver, all methods should then take pointer receivers.

References