Go Interfaces Tutorial

Elliot Forbes Elliot Forbes ⏰ 4 Minutes 📅 Jul 14, 2018

Welcome all, in this tutorial we are going to be taking a look at interfaces within the Go programming language.

By the end of this tutorial, we’ll have covered the following topics:

  • The Basics of Interfaces
  • Defining Your Own Interfaces

There will be a number of challenges at the end of this tutorial that you are free to try and complete on your own machine to try and validate some of the topics we have covered in this tutorial. If you appreciate this method of learning then please feel free to let me know in the comments section below!

Github Source Code - The full source code for this tutorial can be found here: TutorialEdge/Go-Interfaces-Tutorial

Basic Example

If you are new to Go, you may be seeing interface{} a lot in code snippets and tutorials. This can be fairly daunting seeing this at first if you don’t know what interfaces are in Go and what they are used for.

Typically, if you see a function or a method that expects an empty interface, then you can typically pass anything into this function/method.

Let’s see an example of this in action:

package main

import (
    "fmt"
)

func myFunc(a interface{}) {
    fmt.Println(a)
}

func main() {
    var my_age int
    my_age = 25

    myFunc(my_age)
}

If we then go to run this then we should see that it runs successfully and prints out our integer value:

$ go run main.go
25

Why is this Useful?

By defining a function that takes in an interface{}, we essentially give ourselves the flexibility to pass in anything we want. It’s a Go programmers way of saying, this function takes in something, but I don’t necessarily care about its type.

Defining Interfaces

So, what are interfaces? Why do we use them within Go? Well by defining an interface in Go, we essentially define a contract. If we define a type based off this interface then we are forced to implement all of the functions or methods defined within that interface type.

Say, for example, we wanted to define an interface for a Guitarist. We could define our interface to include a PlayGuitar() function like so:

type Guitarist interface {
  // PlayGuitar prints out "Playing Guitar"
  // to the terminal
  PlayGuitar()
}

With our Guitarist interface defined, we could define a BaseGuitarist and an AcousticGuitarist struct which implements the Guitarist interface.

package main

import "fmt"

type Guitarist interface {
    // PlayGuitar prints out "Playing Guitar"
    // to the terminal
    PlayGuitar()
}

type BaseGuitarist struct {
    Name string
}

type AcousticGuitarist struct {
    Name string
}

func (b BaseGuitarist) PlayGuitar() {
    fmt.Printf("%s plays the Bass Guitar\n", b.Name)
}

func (b AcousticGuitarist) PlayGuitar() {
    fmt.Printf("%s plays the Acoustic Guitar\n", b.Name)
}

func main() {
    var player BaseGuitarist
    player.Name = "Paul"
    player.PlayGuitar()

    var player2 AcousticGuitarist
    player2.Name = "Ringo"
    player2.PlayGuitar()
}

Should we wish, we could then create an array of type Guitarist which could store both our BaseGuitarist and AcousticGuitarist objects.

var guitarists []Guitarist
guitarists = append(guitarists, player)
guitarists = append(guitarists, player2)

Return Values

In real-world examples, we would typically have more complex functions within our interfaces that featured return values. In Go, we can define these interfaces like so:

type Employee interface {
    Name() string
    Language() string
    Age() int
    Random() (string, error)
}

Satisfying Interfaces

Say we wanted to create an array of all Employee’s in the firm. Within this array, we’d want all of our Engineers.

Now, in order for this to work, we’d need our Engineer type to satisfy the Employee interface or it will not allow us to compile our program:

package main

type Employee interface {
    Language() string
    Age() int
    Random() (string, error)
}

type Engineer struct {
    Name string
}

func (e Engineer) Language() string {
    return e.Name + " programs in Go"
}

func main() {
    // This will throw an error
    var programmers []Employee
    elliot := Engineer{Name: "Elliot"}
    // Engineer does not implement the Employee interface
    // you'll need to implement Age() and Random()
    programmers = append(programmers, elliot)
}

Conclusion

So, within this tutorial, we have successfully managed to uncover what interfaces are within Go and how we can implement them within our own Go-based programs.

Hopefully, you found this tutorial useful and if you did then please let me know in the comments section below!

Note - If you want to keep track of when new Go articles are posted to the site, then please feel free to follow me on twitter for all the latest news: @Elliot_F.