“How To Use Interfaces in Golang?”

golang
Reading Time: 4 minutes

What is an interface?

Interface types are one special kind of type in Go. An interface in Go is a type defined using a set of method signatures. The interface defines the behavior of a similar type of object.
Go has great support for interfaces and they are implemented in an implicit way. They allow polymorphism in Go. An interface is an abstract concept that enables polymorphism in Go. A variable of that interface can hold the value that implements the type

How to create an interface?

In Go language, you can create an interface using the following syntax:

type interface_name interface{

// Method signatures

}

Let’s see an example to understand more:

package main

import "fmt"

type I interface {
	M()
}

type T struct {
	S string
}

// This method means type T implements the interface I,
// but we don't need to explicitly declare that it does so.
func (t T) M() {
	fmt.Println(t.S)
}

func main() {
	var i I = T{"Hello Folks! Welcome to Knoldus blogs"}
	i.M()
}

Output:

Hello Folks! Welcome to Knoldus blogs

Zero-value of an interface

The zero value of an interface is nil. That means it holds no value and type. The code below shows that.

package main
 
import (
    "fmt"
)
 
func main() {
    var a interface{}
     
    fmt.Println(a)    // <nil>
}

An interface is empty if it has no functions at all. An empty interface holds any type. That’s why it is extremely useful in many cases. 

Interface Types

The interface introduces the concept of static and another one is dynamic. The static type is the interface itself. A dynamic type means that it can hold a reference to different types (e.g. string, int, …) and that it can change at runtime, whereas a static type is checked at compile time and cannot change.

Every variable has a type. That type is either a static type (int, string, bool, map, struct, slice, etc) or an interface type.

An interface can be implemented by any static type (typically by an aliased type).

A variable of an interface type is actually stored in two parts. The first part is the symbolic name of the underlying static type. The second part is the data in the format of that static type.

So if a variable is declared to be of an interface type, that means that its type is dynamic in the sense that the underlying type could end up being any of the static types that implement the interface.

Example:


package main
  
import "fmt"
  
type Shape interface {
  
    Area() float64
    Perimeter() float64
}
  
func main() {
  
    var s Shape
    fmt.Println("Value of s is: ", s)
    fmt.Printf("Type of the s is: %T ", s)
}

Output:

Value of s is:  <nil>
Type of the s is: <nil> 

From the above example, we can see that the zero value and the type of the interface are nil. This is because, at this moment, we have declared the variable s of type Shape but did not assign any value.

When we use Println function from fmt package with interface argument, it points to the dynamic value of the interface and %T syntax in Printf function refers to the dynamic type of interface.

Access Underlying Variable of Interface

The underlying variable can be accessed in two ways

  • Type Assertion
  • Type Switch

Type Assertion

Type assertion provides a way to access the underlying variable inside the interface value of the interface by asserting the correct type of underlying value. Below is the syntax for that where i is an interface:

val := i.({type})

The above statement is asserting that the type of underlying value in the interface is of type {type}. If this is true then the underlying value is assigned to valIf not then the above statement panics.

package main

import "fmt"

func main() {
	var x interface{} = "test-type-assertion"

	var s string = x.(string)
	fmt.Println(s) // "foo"

	s, ok := x.(string)
	fmt.Println(s, ok) // "foo true"

	n, ok := x.(int)
	fmt.Println(n, ok) // "0 false"

	n = x.(int) // ILLEGAL
}

Output:

test-type-assertion
test-type-assertion true
0 false
panic: interface conversion: interface {} is string, not int

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

  • If T is not an interface, it asserts that the dynamic type of x is identical to T.
  • If T is an interface, it asserts that the dynamic type of x implements T.

If the type assertion holds, the value of the expression is the value stored in x and its type is T. If the type assertion is false, a run-time panic occurs. 

Type Switch

In Go interface, a type switch is used to compare the concrete type of an interface with the multiple types provide in the case statements. It is similar to type assertion with only one difference, i.e, the case specifies types, not the values.

Example:

package main
  
import "fmt"
  
func myfun(a interface{}) {
  
    // Using type switch
    switch a.(type) {
  
    case int:
        fmt.Println("Type: int, Value:", a.(int))
    case string:
        fmt.Println("\nType: string, Value: ", a.(string))
    case float64:
        fmt.Println("\nType: float64, Value: ", a.(float64))
    default:
        fmt.Println("\nType not found")
    }
}
  
// Main method
func main() {
  
    myfun("Hello Folks! Welcome to Knoldus blogs")
    myfun(67)
    myfun(true)
}

Output:

Type: string, Value:  Hello Folks! Welcome to Knoldus blogs
Type: int, Value: 67

Type not found

Using interfaces with functions

Interfaces can be passed to functions just like any other type. Here is an example showing the usage of the interface with functions. A great advantage when using an interface is that it allows any type of argument as we can see in this code below.

package main

import (
	"fmt"
)

func f(i interface{}) {
	fmt.Printf("%T\n", i)
}

func main() {
	var a interface{} = "a string"
	var c int = 42
	
	f(a)   // string
	f(c)   // int
}

Pointer to interface

A pointer can point to anything even an interface. When an empty interface is used it returns nil as a value. Here is an example using an interface with pointers.

package main

import "fmt"

type Worker interface {
	Work()
}

type QA struct{}

func (*QA) Work() {
	fmt.Println("Testing...")
}

type Programer struct{}

func (*Programer) Work() {
	fmt.Println("Programming...")
}

func main() {
	var w Worker = &QA{}
	w.Work()
	w = &Programer{}
	w.Work()
}

Why are interfaces needed in Golang?

Interfaces are used in Go where polymorphism is needed. In a function where multiple types can be passed an interface can be used. Interfaces allow Go to have polymorphism. Interfaces are also used to help reduce duplication/boilerplate code.

Interfaces are very useful in case of functions and methods where you need argument of dynamic types, like Println function which accepts any type of values.

When multiple types implement the same interface, it becomes easy to work with them. Hence whenever we can use interfaces, we should.

knoldus

Written by 

I am an DevOps engineer having experience working with the DevOps tool and technologies like Kubernetes, Docker, Ansible, AWS cloud, prometheus, grafana etc. Flexible towards new technologies and always willing to update skills and knowledge to increase productivity.