Development notes

Thoughts, notes and ideas about development

Methods and Interfaces in Go

This post is a small cheatsheet for Methods and Interfaces in Go. Here I provide main features and describe what methods, interfaces, pointer receiver and value receiver are.

There are no detailed explanations or tricky examples about how to use them. For such content please see other resources.

At the end of the page, there is a small list of useful links to read

Methods

method is a function with a special receiver argument. Methods provide a way to add behavior to (almost) any type.

Receiver is a parameter between the func keyword and the function name. It binds the function to the specified type. It should be defined in the same package as the method.

A method cannot be declared with a receiver whose type is defined in another package (which includes the built-in types such as int).

A method may be declared either using a value receiver or a pointer receiver.

Methods with value receiver:

  • a literal syntax looks like (t T)
  • operate on a copy of the value
  • can be called with pointer values because they can be dereferenced first

Methods with pointer receiver:

  • a literal syntax looks like (t *T)
  • operate on actual value
  • cannot be called with values, because the value stored inside an interface has no address

How to chose between pointer and value receiver:

  • use pointer receiver for mutations or to avoid copying the value on each method call because it may be an expensive operation.
  • use the value receiver to create new value/copy (without mutations)

Some more details on Method Sets specification page.

Example of a method with pointer receiver (is taken from the official A Tour of Go):

Interfaces

Interfaces are types that declare behavior and provide polymorphism. There is no keyword for interface implementation in Go. So, a type implements an interface if it implements all interface methods.

It’s a so-named Duck typing interface.

Interfaces in Go are reference types or a header value, the same as:

  • set of slice
  • map
  • channel
  • interface
  • function types.

empty interface type can represent any type. It is common to use the empty interface type in generic data structures. Empty interface type allows both structure and primitive types to be stored.

MongoDB driver uses such approach for inserting documents into collection:

// InsertOne inserts a single document into the collection.
func (coll *Collection) InsertOne(ctx context.Context, document interface{},
	opts ...*options.InsertOneOptions) (*InsertOneResult, error) {

...
}

To test whether an interface value holds a specific type, a type assertion can be used:

t, ok := i.(T)

if i is of type T then t will be the underlying value and ok will be true.

and for several type assertions Type switches can be used:

package main

import "fmt"

type person struct {
	name string
	email string
}
func genericPrint(v interface{}) {
	switch val := v.(type) {
	case float64:
		fmt.Printf("Pinting float64 %f\n", val)
	case string:
		fmt.Printf("Printing string %s\n", val)
	case person:
		fmt.Printf("Printing person with name: %s and email: %s", val.name, val.email)
	default:
		fmt.Printf("Printing default %v\n", val)
	}
}

func main() {
	genericPrint(123567890.787)
	genericPrint("some string value")
	genericPrint(123)
	genericPrint(person{
		name: "John Doe",
		email: "jd@mail.com",
	})
}

Interfaces and type embedding

Go doesn’t have class inheritance but has types embedding within a struct or interface.

In scope of embedding there are inner and outer types:

Inner type’s methods are promoted up to the outer type. As a result, inner's type identifiers may be accessed through values of the outer type.

Outer types do not need to implement interface because of the inner promotion. In other words, the outer type implements the interface because of inner type’s implementation.

In case if outer type needs to override the inner type’s interface behavior, the outer type should implement the interface.

Some more information can be found on Effective Go # Embedding

comments powered by Disqus