ZetCode

Go inteface

last modified October 30, 2020

Go interface tutorial shows how to work with intefaces in Golang.

An inteface is a set of function signatures and it is a specific type. Another type implements an interface by implementing its functions. While languages like Java or C# explicitly implement interfaces, there is no explicit declaration of intent in Go.

The primary task of an interface is to provide only function signatures consisting of the name, input arguments and return types. It is up to a type (e.g. struct type) to implement functions.

Interfaces are also reffered to as exposed APIs or contracts. If a type implements the functions (the APIs) of a sortable interface, we know that it can be sorted; it adheres to the contract of being sortable.

Interfaces specify behaviour. An interface contains only the signatures of the functions, but not their implementation.

Using interfaces can make code clearer, shorter, and more readable.

Go interface example

The following example uses a simple Shape inteface.

geo_shapes.go
package main

import (
    "fmt"
    "math"
)

type Shape interface {
    Area() float64
}

type Rectangle struct {
    Width, Height float64
}

type Circle struct {
    Radius float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

func getArea(shape Shape) {

    fmt.Println(shape.Area())
}

func main() {

    r := Rectangle{Width: 7, Height: 8}
    c := Circle{Radius: 5}

    getArea(r)
    getArea(c)
}

The Shape is a generic geometric form. It cannot be drawn. The Rectangle and Circle are specific geometric forms, which can be drawn and for which we can calculate the area.

type Shape interface {
    Area() float64
}

We define the Shape interface. It has one function signature: Area.

type Rectangle struct {
    Width, Height float64
}

type Circle struct {
    Radius float64
}

We define two types: the Rectangle and the Circle.

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

We define the Area function for the Rectangle and Circle; we say that these two types implement the Shape interface.

func getArea(shape Shape) {

    fmt.Println(shape.Area())
}

The getArea function takes the Shape interface as a parameter. We can pass both the Rectangle and the Circle, because they are both shapes.

$ go run geo_shapes.go 
56
78.53981633974483

This is the output.

Go interface slice

In the following example, we create a slice of Animal interface.

inteface_slice.go
package main

import (
    "fmt"
)

type Animal interface {
    Sound() string
}

type Dog struct {
}

func (d Dog) Sound() string {
    return "Woof!"
}

type Cat struct {
}

func (c Cat) Sound() string {
    return "Meow!"
}

type Cow struct {
}

func (l Cow) Sound() string {
    return "Moo!"
}

func main() {

    animals := []Animal{Dog{}, Cat{}, Cow{}}

    for _, animal := range animals {

        fmt.Println(animal.Sound())
    }
}

The example defines an Animal interface and Dog, Cat, and Cow types.

type Animal interface {
    Sound() string
}

type Dog struct {
}

func (d Dog) Sound() string {
    return "Woof!"
}

The Dog type implements the Sound contract function of the Animal interface.

animals := []Animal{Dog{}, Cat{}, Cow{}}

for _, animal := range animals {

    fmt.Println(animal.Sound())
}

Because all three types implement one common inteface, we can place them into a slice.

$ go run inteface_slice.go 
Woof!
Meow!
Moo!

This is the output.

Go Stringer interface

The Stringer interface is defined in the fmt package. Its String function is invoked when a type is passed to any of the print functions. We can customize the output message of our own types.

type Stringer interface {
    String() string
}

This is the Stringer interface.

stringer.go
package main

import (
    "fmt"
)

type User struct {
    Name       string
    Occupation string
}

func (u User) String() string {

    return fmt.Sprintf("%s is a(n) %s", u.Name, u.Occupation)
}

func main() {

    u1 := User{"John Doe", "gardener"}
    u2 := User{"Roger Roe", "driver"}

    fmt.Println(u1)
    fmt.Println(u2)
}

In the example, we define the String function of the Stringer interface for the User type.

func (u User) String() string {

    return fmt.Sprintf("%s is a(n) %s", u.Name, u.Occupation)
}

The implementation returns a string that tells the user's name and occupation.

$ go run stringer.go 
John Doe is a(n) gardener
Roger Roe is a(n) driver

This is the output.

Go inteface{}

Go interface{} is an empty interface; all types in Go satisfy the empty interface. Any type can be assigned to a variable declared with empty interface.

empty_interface.go
package main

import (
    "fmt"
)

type Body struct {
    Msg interface{}
}

func main() {

    b := Body{"Hello there"}
    fmt.Printf("%#v %T\n", b.Msg, b.Msg)

    b.Msg = 5
    fmt.Printf("%#v %T\n", b.Msg, b.Msg)
}

We have the Msg variable of interface{}. In the example, we assign a string and an integer to the variable.

$ go run empty_interface.go 
"Hello there" string 
5 int

This is the output.

Go type assertion

The type assertion x.(T) asserts that the concrete value stored in x is of type T, and that x is not nil.

type_assertion.go
package main

import (
    "fmt"
)

func main() {

    var val interface{} = "falcon"

    r, ok := val.(string)
    fmt.Println(r, ok)

    r2, ok2 := val.(int)
    fmt.Println(r2, ok2)

    r3 := val.(string)
    fmt.Println(r3)

    //r4 := val.(int)
    //fmt.Println(r4)
}

In the example, we check the type of the val variable.

r, ok := val.(string)
fmt.Println(r, ok)

In the r variable, we have the value. The ok is a boolean that tells if the value is the string type.

//r4 := val.(int)
//fmt.Println(r4)

The commented lines would lead to a panic: interface conversion: interface {} is string, not int.

$ go run type_assertion.go 
falcon true
0 false
falcon

This is the output.

Go type switch

A type switch is used to compare the concrete type of an interface with the multiple types provide in the case statements.

type_switch.go
package main

import "fmt"

type User struct {
    Name string
}

func checkType(a interface{}) {

    switch a.(type) {

    case int:
        fmt.Println("Type: int, Value:", a.(int))
    case string:
        fmt.Println("Type: string, Value:", a.(string))
    case float64:
        fmt.Println("Type: float64, Value:", a.(float64))
    case User:
        fmt.Println("Type: User, Value:", a.(User))
    default:
        fmt.Println("unknown type")
    }
}

func main() {

    checkType(4)
    checkType("falcon")
    checkType(User{"John Doe"})
    checkType(7.9)
    checkType(true)
}

The checkType function determines the type of its parameter in a switch statement.

$ go run type_switch.go 
Type: int, Value: 4
Type: string, Value: falcon
Type: User, Value: {John Doe}
Type: float64, Value: 7.9
unknown type

This is the output.

In this tutorial, we have covered the interface type in Golang.

List all Go tutorials.