Tarhche

Latest

Most viewed

  • X
    ###
    ###
    ###
  • X
    ###
    ###
    ###
  • X
    ###
    ###
    ###
Contact usOpen source
Tarhche | A fresh design

Comments

Authentication required
To submit your comment you must first log in to your account
No comments have been submitted for this article yet!

Interfaces | Golang basics

user avatar

Mahdi.Kh

September 21, 2026

Interfaces | Golang basics
We get familiar with the concept of interfaces and learn how to use interfaces in the Go language.

1

Introduction | Golang basics

2

Setting Up the Development Environment | Golang basics

3

The Structure of Go Programs | Golang basics

4

Variables | Golang basics

5

Constants | Golang basics

6

Numbers | Golang basics

7

Boolean | Golang basics

8

The String Data Type | Golang basics

9

The slice Data Type | Golang basics

10

Loops | Golang basics

11

The map Data Type | Golang basics

12

Functions | Golang basics

13

The struct Data Type | Golang basics

14

Interfaces | Golang basics

15

Generics | Golang basics

16

Defer, Panic, and Recover | Golang basics

17

JSON | Golang basics

18

Converting Data to Binary Using Gob | Golang basics

19

Introducing the Standard RPC Package | Golang basics

20

Error Handling | Golang basics

21

Packages and Modules | Golang basics

22

Reflection | Golang basics

23

Generating Code with go generate | Golang basics

24

Goroutines | Golang basics

25

Channels | Golang basics

26

Context | Golang basics

27

Unit Testing | Golang basics

28

The time Standard Package | Golang basics

29

The sync Standard Package | Golang basics

30

Introducing go fix | Golang basics

31

Writing Documentation and Using Godoc | Golang basics

An interface in programming is a contract or template that specifies which methods a type must implement, without specifying any implementation details. 

An interface essentially describes the behavior or capabilities an object should have.

In Go, interfaces are a data type that only defines methods' signatures and has no implementation.

type Reader interface {
	Read(p []byte) (n int, err error)
}

In Go, implementing interfaces is done implicitly, meaning that if a type has all the methods defined in an interface, it has implemented that interface.

In the example below, File automatically implements Writer, because it has all of its methods:

type Writer interface {
	Write(data []byte) error
}

type File struct{}

func (f File) Write(data []byte) error {
	fmt.Println("writing to file:", string(data))
}

Type Assertion, or Interface Assertion

Type assertion, or interface assertion, is a mechanism that lets a developer extract the concrete type of a value that has an interface type.

The general syntax of a type assertion is as follows:

// will panic if i is not an instance of T
value := i.(T)
  • i is a value of type interface.

  • T is the type we expect the value i to actually be.

  • value is the variable that the extracted value of type T is stored in.

If the value i is actually of type T, the operation succeeds. Otherwise, the program will panic at runtime (a runtime panic).

To avoid a panic and safely check the value's type, you can use the second form of type assertion:

// the ok will be false if i is not an instance of T
value, ok := i.(T)
  • If the value i is of type T, the variable ok will be true.

  • Otherwise, ok will be , and will hold the zero value of type .

#golang#go
false
value
T

In Go, this pattern is known as the comma-ok idiom, and it's a common way to do a type assertion safely.

Now let's look at some real examples:

package main

import "fmt"

func main() {
	var data interface{} = "Hello world"

	str := data.(string)
	fmt.Println(str)

	num, ok := data.(int)
	if ok {
		fmt.Println(num)
	} else {
		fmt.Println("the value is not a number!")
	}
}

Also, if you've created your own custom interface, you can do the same thing:

package main

import (
	"fmt"
	"math"
)

// with this interface we can calculate the area
type Shape interface {
	Area() float64
}

// circle type
type Circle struct{
	 Radius float64
}

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

// rectangle type
type Rectangle struct {
	Width float64
	Height float64
}

func (c Rectangle) Area() float64 {
	return math.Pi * c.Width * c.Height
}

// this function shows some information based on data type
func DescribeShape(s Shape) {
	if c, ok := s.(Circle); ok {
		fmt.Printf("This is a circle with radius %f\n", c.Radius)
	}

	if r, ok := s.(Rectangle); ok {
		fmt.Printf("This is a rectangle with length %f and width %f\n", r.Height, r.Width)	
	}
}

func main() {
	var shapes = []Shape {
		Circle{
			Radius: 3,
		},
		Rectangle{
			Width: 4,
			Height: 5,
		},
	}

	for _, s := range shapes {
		DescribeShape(s)
		fmt.Println("Area:", s.Area())
	}
}

In the example above, we used type assertion inside the DescribeShape function.

Type assertion is also useful for checking whether a value implements a specific interface. In this approach, we need to declare our variable as an interface type, or convert it to type interface using a type conversion, then put the value we want into that variable, and finally use a type assertion to check whether that data type implements the interface we want. Take a look at the example below:

package main

import "fmt"

type Speaker interface {
	Speak()
}

type Human struct{
	Name string
}

func (h Human) Speak() {
	fmt.Println("Hi, my name is ", h.Name)
}

func main() {
	var h interface{}

	h = Human{
		Name: "Mahdi",
	}
	
	if i, ok := interface{}(h).(Speaker); ok {
		i.Speak()
	} else {
		fmt.Println("This is not an speaker")
	}
}

Using a Type Switch

To check the actual type of a value that has an interface type, besides type assertion, you can use a type switch.

package main

import "fmt"

func main() {
	w := 10
	printType(w)

	x := 3.2
	printType(x)

	y := "hello"
	printType(y)

	z := struct { Name string } {
		Name: "Mahdi",
	}
	printType(z)
}

func printType(data interface{}) {
	switch v := data.(type) {
	case int:
		fmt.Println("integer number:", v)
	case float32, float64:
		fmt.Println("float number:", v)
	case string:
		fmt.Println("string:", v)
	case bool:
		fmt.Println("boolean:", v)
	default:
		fmt.Printf("unknown %#v\n", v)
	}
}

The Empty Interface

The empty interface, interface{}, means "any type." Because it has no methods, every type in Go implements it by default.

The empty interface can hold any kind of data, so wherever you need to be able to accept any kind of data, you can use the empty interface.

package main

import "fmt"

func main() {
	PrintAny(10)
	PrintAny("Hello, world")		
}

func PrintAny(data interface{}) {
	fmt.Println(data)
}

In Go 1.18, the any type was added to the language, and it's really just an alias for interface{}, but it makes code look more readable.

var x interface{} = 10
var y any = 10

any is like a nicer name for interface{} that makes the code more readable.

Drawbacks of Using the Empty Interface

  1. The empty interface, interface{}, is a form of type erasure, meaning the actual type is hidden, and to use it again you need to use a type assertion or a type switch.

  2. Using interface{} excessively or without good reason makes the code lose readability and type safety, because during development we need to check the older code to make sure what type of data was passed to the interface.

Interface Composition

In Go, instead of inheritance, composition is used to build more complex structs and interfaces. This way, several interfaces can be combined to build a larger interface.

Suppose we have two separate interfaces:

type Reader interface {
	Read(p []byte) (n int, err error)
}

type Writer interface {
	Write(p []byte) (n int, err error)
}

Now we want to build a new interface that has both Read and Write:

type ReadWriter interface {
	Reader
	Writer
}

Here, ReadWriter is a composition of Reader and Writer. Any type that implements both Reader and Writer has also implemented ReadWriter.

In Go, it's recommended that, instead of designing large interfaces, you design small interfaces with one or two methods and combine them through composition when needed. This is one of Go's well-known principles:

Design small interfaces. Compose them when needed

 


Previous part: The struct Data Type | Golang basics

Next part: Generics | Golang basics