13. interface

Be Aware:

Learning to use interfaces in Go is extremely difficult - the basics are easy but it will take some practice before you know how to design your own interfaces.

An interface is a set of methods. To turn it around, the methods implemented by a concrete type such as a struct form the interface of that type.

Let’s focus on the method set aspect of interfaces first.

In Go, suppose you have an interface for a Duck:

1 type Duck interface {
2 	Quack()
3 }

Any struct that has a Quack method will implement the Duck interface implicitly and can be used as a Duck.

 1 package main
 2 
 3 import "fmt"
 4 
 5 type Duck interface {
 6         Quack()
 7 }
 8 
 9 type Donald struct {
10 }
11 
12 func (d Donald) Quack() {
13         fmt.Println("quack quack!")
14 }
15 
16 type Daisy struct {
17 }
18 
19 func (d Daisy) Quack() {
20         fmt.Println("~quack ~quack")
21 }
22 
23 func main() {
24         var duck Duck
25         duck = Donald{}
26         duck.Quack()
27 
28         duck = Daisy{}
29         duck.Quack()
30 }

with output:

quack quack!
~quack ~quack

If you have another struct, Dog, which doesn’t have a Quack method and you tried to use it as a Duck then you’ll get a com­pile time error:

 1 package main
 2 
 3 import "fmt"
 4 
 5 type Duck interface {
 6         Quack()
 7 }
 8 
 9 type Donald struct {
10 }
11 
12 func (d Donald) Quack() {
13         fmt.Println("quack quack!")
14 }
15 
16 type Daisy struct {
17 }
18 
19 func (d Daisy) Quack() {
20         fmt.Println("~quack ~quack")
21 }
22 
23 type Dog struct {
24 }
25 
26 func (d Dog) Bark() {
27         fmt.Println("woof woof")
28 }
29 
30 func main() {
31         var duck Duck
32         duck = Donald{}
33         duck.Quack()
34 
35         duck = Daisy{}
36         duck.Quack()
37 
38         duck = Dog{}
39         duck.Quack()
40 }

with output:

main.go:38: cannot use Dog literal (type Dog) as type Duck in assignment:
Dog does not implement Duck (missing Quack method)

Earlier we had seen an example of a Square struct and an Area function:

 1 package main
 2 
 3 import "fmt"
 4 
 5 type Square struct {
 6         Width, Height int
 7 }
 8 
 9 func (s Square) Area() int {
10         return s.Width * s.Height
11 }
12 
13 func main() {
14         r1 := Square{5, 5}
15         fmt.Println("Square is: ", r1)
16         fmt.Println("Square area is: ", r1.Area())
17 }

with output:

Square is:  {5 5}
Square area is:  25

Note: Although not used here, it is idiomatic in Go for interface names to be suffixed with “-er” (like Positioner, Distancer).

Let us now abstract out the Area function into an interface called Geometry. Geometry is an interface and has a single function Area that returns an int as follows:

1 type Geometry interface {
2         Area() int
3 }

Let us write the full program:

Program: geometry.go


 1 package main
 2 
 3 import "fmt"
 4 
 5 type Square struct {
 6 	Width, Height int
 7 }
 8 
 9 type Geometry interface {
10 	Area() int
11 }
12 
13 // To implement an interface in Go, we just need to implement all the methods
14 // in the interface
15 func (s Square) Area() int {
16 	return s.Width * s.Height
17 }
18 
19 // If a variable has an interface type, then we can call methods that are in
20 // the named interface. Here is a generic measure function taking advantage of
21 // this to work on any Geometry.
22 func measure(g Geometry) {
23 	fmt.Println(g)
24 	fmt.Println(g.Area())
25 }
26 
27 func main() {
28 	s := Square{Width: 3, Height: 4}
29 
30 	// The Square struct type implements the Geometry interface so we can
31 	// use instances of these structs as arguments to measure.
32 	measure(s)
33 }

The output is:

{3 4}
12

Here’s an example1 of a simple Animal interface.

13.1 The interface{} type

It’s possible to define an interface without any methods. Such an interface is known as an empty interface, and it’s denoted by interface{}. Since there are no methods, all types will satisfy this interface.

That means if you write a function that takes an interface{} value as a parameter, you can supply that function with any value. So, this function:

1 func DoSomething(v interface{}) {
2         // ...
3 }

will accept any parameter whatsoever.

Here’s where it gets confusing: inside of the DoSomething function, what is v’s type? Beginners are led to believe that “v is of any type”, but that is wrong. v is not of any type; it is of interface{} type. Wait, what? When passing a value into the DoSomething function, the Go runtime will perform a type conversion (if necessary), and convert the value to an interface{} value. All values have exactly one type at runtime, and v’s one static type is interface{}.

This should leave you wondering: ok, so if a conversion is taking place, what is actually being passed into a function that takes an interface{} value?

If you understand that an interface value is two words wide and it contains a pointer to the underlying data, that’s typically enough to avoid common pitfalls.

This topic is adapted from blog posts this2 and this3

Remember:

Can’t use nil to initialize a variable without an explicit type

The nil identifier can be used as the “zero value” for interfaces, functions, pointers, maps, slices, and channels. If you don’t specify the variable type the compiler will fail to compile your code because it can’t guess the type.

package main

func main() {  
        var x = nil //error

        _ = x
}

Exercise 15

Declare an interface named Speaker with a method named SayHello. Declare a struct named English that represents a person who speaks english, declare a struct named Chinese for someone who speaks chinese and a struct named Indian for someone who speaks hindi. Implement the Speaker interface for each struct using a pointer receiver and these literal strings “I speak English!”, “I speak Chinese!” and “I speak Hindi!”.

13.2 Solutions

Exercise 15 Solution4.

  1. http://play.golang.org/p/yGTd4MtgD5
  2. http://jordanorelli.tumblr.com/post/32665860244/how-to-use-interfaces-in-go
  3. http://theburningmonk.com/2015/05/why-i-like-golang-interfaces/
  4. http://play.golang.org/p/VABl9UNTOs